mirror of
https://github.com/scratchfoundation/scratch-flash.git
synced 2024-12-04 21:21:06 -05:00
Added speech bubble with value of reporter when clicked
This commit is contained in:
parent
9be7d2e86e
commit
ca09a56cbb
5 changed files with 109 additions and 28 deletions
|
@ -132,6 +132,7 @@ public class Scratch extends Sprite {
|
|||
stage.addEventListener(MouseEvent.MOUSE_DOWN, gh.mouseDown);
|
||||
stage.addEventListener(MouseEvent.MOUSE_MOVE, gh.mouseMove);
|
||||
stage.addEventListener(MouseEvent.MOUSE_UP, gh.mouseUp);
|
||||
stage.addEventListener(MouseEvent.MOUSE_WHEEL, gh.mouseWheel);
|
||||
stage.addEventListener('rightClick', gh.rightMouseClick);
|
||||
stage.addEventListener(KeyboardEvent.KEY_DOWN, runtime.keyDown);
|
||||
stage.addEventListener(KeyboardEvent.KEY_UP, runtime.keyUp);
|
||||
|
@ -813,6 +814,12 @@ public class Scratch extends Sprite {
|
|||
|
||||
public function handleTool(tool:String, evt:MouseEvent):void { }
|
||||
|
||||
public function showBubble(text:String, x:* = null, y:* = null, width:Number = 0):void {
|
||||
if (x == null) x = mouseX;
|
||||
if (y == null) y = mouseY;
|
||||
gh.showBubble(text, Number(x), Number(y), width);
|
||||
}
|
||||
|
||||
// -----------------------------
|
||||
// Project Management and Sign in
|
||||
//------------------------------
|
||||
|
|
|
@ -58,6 +58,7 @@
|
|||
package interpreter {
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.getTimer;
|
||||
import flash.geom.Point;
|
||||
import blocks.*;
|
||||
import primitives.*;
|
||||
import scratch.*;
|
||||
|
@ -116,7 +117,8 @@ public class Interpreter {
|
|||
currentMSecs = getTimer();
|
||||
var oldThread:Thread = activeThread;
|
||||
activeThread = new Thread(b, targetObj);
|
||||
app.log(evalCmd(b));
|
||||
var p:Point = b.localToGlobal(new Point(0, 0));
|
||||
app.showBubble(String(evalCmd(b)), p.x, p.y, b.width);
|
||||
activeThread = oldThread;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -561,7 +561,7 @@ public class ScratchSprite extends ScratchObj {
|
|||
}
|
||||
if (!(s is String)) s = s.toString();
|
||||
if (s.length == 0) return;
|
||||
bubble = new TalkBubble(s, type, isAsk);
|
||||
bubble = new TalkBubble(s, type, isAsk ? 'ask' : 'say');
|
||||
parent.addChild(bubble);
|
||||
updateBubble();
|
||||
}
|
||||
|
|
|
@ -26,16 +26,39 @@ public class TalkBubble extends Sprite {
|
|||
public var pointsLeft:Boolean;
|
||||
|
||||
private var type:String; // 'say' or 'think'
|
||||
private var style:String; // 'say' or 'ask' or 'result'
|
||||
private var shape:Shape;
|
||||
private var text:TextField;
|
||||
private static var textFormat:TextFormat = new TextFormat(CSS.font, 14, 0, true, null, null, null, null, TextFormatAlign.CENTER);
|
||||
private static var resultFormat:TextFormat = new TextFormat(CSS.font, 12, CSS.textColor, null, null, null, null, null, TextFormatAlign.CENTER);
|
||||
private var outlineColor:int = 0xA0A0A0;
|
||||
private var radius:int = 8; // corner radius
|
||||
private var padding:int = 5;
|
||||
private var minWidth:int = 55;
|
||||
private var lastXY:Array;
|
||||
private var pInset1:int = 16
|
||||
private var pInset2:int = 50
|
||||
private var pDrop:int = 17;
|
||||
private var pDropX:int = 8;
|
||||
private var lineWidth:Number = 3;
|
||||
|
||||
public function TalkBubble(s:String, type:String, isAsk:Boolean) {
|
||||
public function TalkBubble(s:String, type:String, style:String) {
|
||||
this.type = type;
|
||||
this.style = style;
|
||||
if (style == 'ask') {
|
||||
outlineColor = 0x4AADDE;
|
||||
} else if (style == 'result') {
|
||||
outlineColor = 0x888888;
|
||||
minWidth = 16;
|
||||
padding = 3;
|
||||
radius = 5;
|
||||
pInset1 = 8;
|
||||
pInset2 = 16;
|
||||
pDrop = 5;
|
||||
pDropX = 4;
|
||||
lineWidth = 0.5;
|
||||
}
|
||||
pointsLeft = true;
|
||||
if (isAsk) outlineColor = 0x4AADDE;
|
||||
shape = new Shape();
|
||||
addChild(shape);
|
||||
text = makeText();
|
||||
|
@ -49,7 +72,7 @@ public class TalkBubble extends Sprite {
|
|||
var newValue:Boolean = (dir == 'left');
|
||||
if (pointsLeft == newValue) return;
|
||||
pointsLeft = newValue;
|
||||
setWidthHeight(text.width + 10, text.height + 10);
|
||||
setWidthHeight(text.width + padding * 2, text.height + padding * 2);
|
||||
}
|
||||
|
||||
public function getText():String { return text.text }
|
||||
|
@ -58,30 +81,28 @@ public class TalkBubble extends Sprite {
|
|||
var desiredWidth:int = 135;
|
||||
text.width = desiredWidth + 100; // wider than desiredWidth
|
||||
text.text = s;
|
||||
text.width = Math.max(55, Math.min(text.textWidth + 8, desiredWidth)); // fix word wrap
|
||||
setWidthHeight(text.width + 10, text.height + 10);
|
||||
text.width = Math.max(minWidth, Math.min(text.textWidth + 8, desiredWidth)); // fix word wrap
|
||||
setWidthHeight(text.width + padding * 2, text.height + padding * 2);
|
||||
}
|
||||
|
||||
private function setWidthHeight(w:int, h:int):void {
|
||||
var g:Graphics = shape.graphics;
|
||||
g.clear();
|
||||
g.beginFill(0xFFFFFF);
|
||||
g.lineStyle(3, outlineColor);
|
||||
g.lineStyle(lineWidth, outlineColor);
|
||||
if (type == 'think') drawThink(w, h);
|
||||
else drawTalk(w, h);
|
||||
}
|
||||
|
||||
private function makeText():TextField {
|
||||
var format:TextFormat = new TextFormat(CSS.font, 14, 0, true);
|
||||
format.align = TextFormatAlign.CENTER;
|
||||
var result:TextField = new TextField();
|
||||
result.autoSize = TextFieldAutoSize.LEFT;
|
||||
result.defaultTextFormat = format;
|
||||
result.defaultTextFormat = style == 'result' ? resultFormat : textFormat;
|
||||
result.selectable = false; // not selectable
|
||||
result.type = 'dynamic'; // not editable
|
||||
result.wordWrap = true;
|
||||
result.x = 5;
|
||||
result.y = 5;
|
||||
result.x = padding;
|
||||
result.y = padding;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -89,7 +110,6 @@ public class TalkBubble extends Sprite {
|
|||
var insetW:int = w - radius;
|
||||
var insetH:int = h - radius;
|
||||
// pointer geometry:
|
||||
var pInset1:int = 16, pInset2:int = 50, pDrop:int = 17;
|
||||
startAt(radius, 0);
|
||||
line(insetW, 0);
|
||||
arc(w, radius);
|
||||
|
@ -97,11 +117,11 @@ public class TalkBubble extends Sprite {
|
|||
arc(insetW, h);
|
||||
if (pointsLeft) {
|
||||
line(pInset2, h);
|
||||
line(8, h + pDrop);
|
||||
line(pDropX, h + pDrop);
|
||||
line(pInset1, h);
|
||||
} else {
|
||||
line(w - pInset1, h);
|
||||
line(w - 8, h + pDrop);
|
||||
line(w - pDropX, h + pDrop);
|
||||
line(w - pInset2, h);
|
||||
}
|
||||
line(radius, h);
|
||||
|
|
|
@ -49,8 +49,8 @@
|
|||
package util {
|
||||
import flash.display.*;
|
||||
import flash.events.MouseEvent;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.filters.*;
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.filters.*;
|
||||
import flash.geom.*;
|
||||
import flash.text.*;
|
||||
import flash.utils.getTimer;
|
||||
|
@ -73,6 +73,7 @@ public class GestureHandler {
|
|||
private var originalScale:Number;
|
||||
|
||||
private var app:Scratch;
|
||||
private var stage:Stage;
|
||||
private var dragClient:DragClient;
|
||||
private var mouseDownTime:uint;
|
||||
private var gesture:String = "idle";
|
||||
|
@ -81,13 +82,20 @@ public class GestureHandler {
|
|||
private var mouseDownEvent:MouseEvent;
|
||||
private var inIE:Boolean;
|
||||
|
||||
private var bubble:TalkBubble;
|
||||
private var bubbleStartX:Number;
|
||||
private var bubbleStartY:Number;
|
||||
private static var bubbleRange:Number = 25;
|
||||
private static var bubbleMargin:Number = 5;
|
||||
|
||||
public function GestureHandler(app:Scratch, inIE:Boolean) {
|
||||
this.app = app;
|
||||
this.stage = app.stage;
|
||||
this.inIE = inIE;
|
||||
}
|
||||
|
||||
public function setDragClient(newClient:DragClient, evt:MouseEvent):void {
|
||||
Menu.removeMenusFrom(app.stage);
|
||||
Menu.removeMenusFrom(stage);
|
||||
if (dragClient != null) dragClient.dragEnd(evt);
|
||||
dragClient = newClient as DragClient;
|
||||
dragClient.dragBegin(evt);
|
||||
|
@ -124,12 +132,12 @@ public class GestureHandler {
|
|||
public function rightMouseDown(x:int, y:int, isChrome:Boolean):void {
|
||||
// To avoid getting the Adobe menu on right-click, JavaScript captures
|
||||
// right-button mouseDown events and calls this method.'
|
||||
Menu.removeMenusFrom(app.stage);
|
||||
Menu.removeMenusFrom(stage);
|
||||
var menuTarget:* = findTargetFor('menu', app, x, y);
|
||||
if (!menuTarget) return;
|
||||
try { var menu:Menu = menuTarget.menu(new MouseEvent('right click')) } catch (e:Error) {}
|
||||
if (menu) menu.showOnStage(app.stage, x, y);
|
||||
if (!isChrome) Menu.removeMenusFrom(app.stage); // hack: clear menuJustCreated because there's no rightMouseUp
|
||||
if (menu) menu.showOnStage(stage, x, y);
|
||||
if (!isChrome) Menu.removeMenusFrom(stage); // hack: clear menuJustCreated because there's no rightMouseUp
|
||||
}
|
||||
|
||||
private function findTargetFor(property:String, obj:*, x:int, y:int):DisplayObject {
|
||||
|
@ -150,6 +158,7 @@ public class GestureHandler {
|
|||
ExternalInterface.call('tip_bar_api.fixIE');
|
||||
|
||||
evt.updateAfterEvent(); // needed to avoid losing display updates with later version of Flash 11
|
||||
hideBubble();
|
||||
mouseIsDown = true;
|
||||
if (gesture == 'clickOrDoubleClick') {
|
||||
handleDoubleClick(mouseDownEvent);
|
||||
|
@ -218,6 +227,13 @@ public class GestureHandler {
|
|||
spr.scratchY = 180 - stageP.y;
|
||||
spr.updateBubble();
|
||||
}
|
||||
if (bubble) {
|
||||
var dx:Number = bubbleStartX - app.mouseX;
|
||||
var dy:Number = bubbleStartY - app.mouseY;
|
||||
if (dx * dx + dy * dy > bubbleRange * bubbleRange) {
|
||||
hideBubble();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function mouseUp(evt:MouseEvent):void {
|
||||
|
@ -230,7 +246,7 @@ public class GestureHandler {
|
|||
return;
|
||||
}
|
||||
drop(evt);
|
||||
Menu.removeMenusFrom(app.stage);
|
||||
Menu.removeMenusFrom(stage);
|
||||
if (gesture == "unknown") {
|
||||
if (mouseTarget && ('doubleClick' in mouseTarget)) gesture = "clickOrDoubleClick";
|
||||
else {
|
||||
|
@ -251,6 +267,10 @@ public class GestureHandler {
|
|||
}
|
||||
}
|
||||
|
||||
public function mouseWheel(evt:MouseEvent):void {
|
||||
hideBubble();
|
||||
}
|
||||
|
||||
private function findMouseTarget(evt:MouseEvent, target:*):DisplayObject {
|
||||
// Find the mouse target for the given event. Return null if no target found.
|
||||
|
||||
|
@ -266,7 +286,7 @@ public class GestureHandler {
|
|||
}
|
||||
o = o.parent;
|
||||
}
|
||||
var rect:Rectangle = app.stageObj().getRect(app.stage);
|
||||
var rect:Rectangle = app.stageObj().getRect(stage);
|
||||
if(!mouseTarget && rect.contains(evt.stageX, evt.stageY)) return findMouseTargetOnStage(evt.stageX / app.scaleX, evt.stageY / app.scaleY);
|
||||
if (o == null) return null;
|
||||
if ((o is Block) && Block(o).isEmbeddedInProcHat()) return o.parent;
|
||||
|
@ -315,7 +335,7 @@ public class GestureHandler {
|
|||
|
||||
private function handleDrag(evt:MouseEvent):void {
|
||||
// Note: Called with a null event if gesture is click and hold.
|
||||
Menu.removeMenusFrom(app.stage);
|
||||
Menu.removeMenusFrom(stage);
|
||||
if (!('objToGrab' in mouseTarget)) return;
|
||||
if (!app.editMode) {
|
||||
if ((mouseTarget is ScratchSprite) && !ScratchSprite(mouseTarget).isDraggable) return; // don't drag locked sprites in presentation mode
|
||||
|
@ -345,7 +365,7 @@ public class GestureHandler {
|
|||
if (mouseTarget == null) return;
|
||||
var menu:Menu;
|
||||
try { menu = mouseTarget.menu(evt) } catch (e:Error) {}
|
||||
if (menu) menu.showOnStage(app.stage, evt.stageX / app.scaleX, evt.stageY / app.scaleY);
|
||||
if (menu) menu.showOnStage(stage, evt.stageX / app.scaleX, evt.stageY / app.scaleY);
|
||||
}
|
||||
|
||||
private var lastGrowShrinkSprite:Sprite;
|
||||
|
@ -409,7 +429,7 @@ public class GestureHandler {
|
|||
}
|
||||
|
||||
if (app.editMode) addDropShadowTo(obj);
|
||||
app.stage.addChild(obj);
|
||||
stage.addChild(obj);
|
||||
obj.x = globalP.x;
|
||||
obj.y = globalP.y;
|
||||
if (evt && mouseDownEvent) {
|
||||
|
@ -425,7 +445,7 @@ public class GestureHandler {
|
|||
// Search for an object to handle this drop and return true one is found.
|
||||
// Note: Search from front to back, so the front-most object catches the dropped object.
|
||||
if(app.isIn3D) app.stagePane.visible = true;
|
||||
var possibleTargets:Array = app.stage.getObjectsUnderPoint(new Point(evt.stageX / app.scaleX, evt.stageY / app.scaleY));
|
||||
var possibleTargets:Array = stage.getObjectsUnderPoint(new Point(evt.stageX / app.scaleX, evt.stageY / app.scaleY));
|
||||
if(app.isIn3D) {
|
||||
app.stagePane.visible = false;
|
||||
if(possibleTargets.length == 0 && app.stagePane.scrollRect.contains(app.stagePane.mouseX, app.stagePane.mouseY))
|
||||
|
@ -484,6 +504,38 @@ public class GestureHandler {
|
|||
o.filters = newFilters;
|
||||
}
|
||||
|
||||
public function showBubble(text:String, x:Number, y:Number, width:Number = 0):void {
|
||||
hideBubble();
|
||||
bubble = new TalkBubble(text || ' ', 'say', 'result');
|
||||
bubbleStartX = app.mouseX;
|
||||
bubbleStartY = app.mouseY;
|
||||
var bx:Number = x + width;
|
||||
var by:Number = y - bubble.height;
|
||||
if (bx + bubble.width > stage.stageWidth - bubbleMargin && x - bubble.width > bubbleMargin) {
|
||||
bx = x - bubble.width;
|
||||
bubble.setDirection('right');
|
||||
} else {
|
||||
bubble.setDirection('left');
|
||||
}
|
||||
bubble.x = Math.max(bubbleMargin, Math.min(stage.stageWidth - bubbleMargin, bx));
|
||||
bubble.y = Math.max(bubbleMargin, Math.min(stage.stageHeight - bubbleMargin, by));
|
||||
|
||||
var f:DropShadowFilter = new DropShadowFilter();
|
||||
f.distance = 4;
|
||||
f.blurX = f.blurY = 8;
|
||||
f.alpha = 0.2;
|
||||
bubble.filters = bubble.filters.concat(f);
|
||||
|
||||
stage.addChild(bubble);
|
||||
}
|
||||
|
||||
public function hideBubble():void {
|
||||
if (bubble) {
|
||||
stage.removeChild(bubble);
|
||||
bubble = null;
|
||||
}
|
||||
}
|
||||
|
||||
/* Debugging */
|
||||
|
||||
private var debugSelection:DisplayObject;
|
||||
|
|
Loading…
Reference in a new issue