-
Notifications
You must be signed in to change notification settings - Fork 35
Expand file tree
/
Copy pathRBHighlightHandler.java
More file actions
206 lines (175 loc) · 6.23 KB
/
Copy pathRBHighlightHandler.java
File metadata and controls
206 lines (175 loc) · 6.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the MIT License https://raw.github.com/mit-cml/app-inventor/master/mitlicense.txt
package openblocks.renderable;
import java.awt.AlphaComposite;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Container;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.geom.Area;
import java.awt.image.BufferedImage;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import openblocks.workspace.RBParent;
import openblocks.codeblockutil.GraphicsManager;
/**
* The RBHighlightHandler class is responsible for maintaining the
* highlight state of a RenderableBlock and rendering the highlight
* properly. It works by adding itself to a layer behind its RenderableBlock
* and drawing the highlight there so as not to cover the block.
*/
public class RBHighlightHandler extends JComponent implements ComponentListener, HierarchyListener {
private static final long serialVersionUID = 328149080427L;
//highlight stroke and width specifications
public static final int HIGHLIGHT_STROKE_WIDTH = 12;
private static final float HIGHLIGHT_ALPHA = .75f;
private static final Color HIGHLIGHT_COLOR = new Color(255,165,0);
private Color hColor = null;
private boolean isSearchResult = false, hasFocus = false;
private Area blockArea = null;
private RenderableBlock rb;
private BufferedImage hImage;
public RBHighlightHandler(RenderableBlock rb) {
super();
this.rb = rb;
setSize(HIGHLIGHT_STROKE_WIDTH/2,1);
this.setOpaque(false);
hColor = null;
this.blockArea = rb.getBlockArea();
updateImage();
rb.addHierarchyListener(this);
rb.addComponentListener(this);
}
public void setHighlightColor(Color c) {
hColor = c;
updateImage();
repaint();
}
public void setIsSearchResult(boolean isResult) {
isSearchResult = isResult;
updateImage();
repaint();
}
public void resetHighlight() {
hColor = null;
updateImage();
repaint();
}
public void repaint() {
if (rb.isVisible()) {
if (blockArea == null || blockArea != rb.getBlockArea() || (rb.getBlock() != null && rb.getBlock().hasFocus() != hasFocus)) {
updateImage();
blockArea = rb.getBlockArea();
}
// only update bounds on the Swing thread
SwingUtilities.invokeLater(new Runnable() {
public void run() {
updateBounds();
}
});
}
super.repaint();
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(new Color(0,0,0,0));
g2.fillRect(0,0,getWidth(),getHeight());
g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,HIGHLIGHT_ALPHA));
if (hImage != null)
g2.drawImage(hImage,0,0,null);
}
public void setParent(RBParent newParent) {
removeFromParent();
newParent.addToHighlightLayer(this);
updateImage();
((Container)newParent).validate();
}
public void removeFromParent() {
if (this.getParent() != null) {
this.getParent().remove(this);
}
}
public void updateImage() {
// cache the focus so it'll know when it needs to redraw later.
hasFocus = rb.getBlock().hasFocus();
Color color = null;
if (hColor != null) {
color = hColor;
}
else if (rb.getBlock().hasFocus()) {
color = HIGHLIGHT_COLOR;
}
else if (isSearchResult) {
color = Color.YELLOW;
}
else if (rb.getBlock().isBad()) {
color = Color.RED;
}
else {
GraphicsManager.recycleGCCompatibleImage(hImage);
hImage = null;
return; // if we're not highlighting, destroy the image and just return
}
if (!rb.isVisible()) {
GraphicsManager.recycleGCCompatibleImage(hImage);
hImage = null;
return; // if we're not highlighting, destroy the image and just return
}
hImage = GraphicsManager.getGCCompatibleImage(rb.getBlockWidth()+HIGHLIGHT_STROKE_WIDTH, rb.getBlockHeight()+HIGHLIGHT_STROKE_WIDTH);
Graphics2D hg = (Graphics2D)hImage.getGraphics();
hg.addRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
hg.setColor(color);
hg.translate(HIGHLIGHT_STROKE_WIDTH/2,HIGHLIGHT_STROKE_WIDTH/2);
hg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER,.2f));
for (int i = 0; i < HIGHLIGHT_STROKE_WIDTH; i++) {
hg.setStroke(new BasicStroke(i));
hg.draw(rb.getBlockArea());
}
hg.setColor(new Color(0,0,0,0));
hg.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC,1));
hg.fill(rb.getBlockArea());
}
/**
* Resizes and moves this highlight to match its RB's size and position
*/
private void updateBounds() {
Point rbLoc = SwingUtilities.convertPoint(rb.getParent(), rb.getLocation(), this.getParent());
this.setBounds(rbLoc.x-HIGHLIGHT_STROKE_WIDTH/2, rbLoc.y-HIGHLIGHT_STROKE_WIDTH/2, rb.getBlockWidth()+HIGHLIGHT_STROKE_WIDTH, rb.getBlockHeight()+HIGHLIGHT_STROKE_WIDTH);
}
/************************************************************
* ComponentListener methods for when the RB moves or resizes
************************************************************/
public void componentResized(ComponentEvent arg0) {
repaint();
}
public void componentMoved(ComponentEvent arg0) {
repaint();
}
public void componentShown(ComponentEvent arg0) {
repaint();
}
public void componentHidden(ComponentEvent arg0) {
GraphicsManager.recycleGCCompatibleImage(hImage);
hImage = null;
}
/*************************************************************
* HierarchyListener method for the RB is added to or removed
* from a parent component
*************************************************************/
public void hierarchyChanged(HierarchyEvent he) {
if (rb.getParent() == null) {
this.removeFromParent();
GraphicsManager.recycleGCCompatibleImage(hImage);
hImage = null;
}
}
}