package bluej.stride.generic;
import bluej.Config;
import bluej.parser.entity.EntityResolver;
import bluej.stride.framedjava.ast.JavadocUnit;
import bluej.stride.framedjava.ast.NameDefSlotFragment;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.framedjava.elements.ImportElement;
import bluej.stride.framedjava.elements.TopLevelCodeElement;
import bluej.stride.framedjava.errors.CodeError;
import bluej.stride.framedjava.frames.CodeFrame;
import bluej.stride.framedjava.frames.ImportFrame;
import bluej.stride.framedjava.frames.StrideDictionary;
import bluej.stride.framedjava.frames.TopLevelFrame;
import bluej.stride.slots.ClassNameDefTextSlot;
import bluej.stride.slots.EditableSlot;
import bluej.stride.slots.Focus;
import bluej.stride.slots.HeaderItem;
import bluej.stride.slots.SlotLabel;
import bluej.stride.slots.SlotTraversalChars;
import bluej.stride.slots.TextSlot;
import bluej.stride.slots.TriangleLabel;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.MultiListener;
import bluej.utility.javafx.SharedTransition;
import bluej.utility.javafx.binding.DeepListBinding;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.ReadOnlyDoubleWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableStringValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import threadchecker.OnThread;
import threadchecker.Tag;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Properties;
import java.util.stream.Stream;
| A top level class to represent common features in Class and Interface frames
| @author Amjad Altadmri
|
public abstract class TopLevelDocumentMultiCanvasFrame<ELEMENT extends CodeElement & TopLevelCodeElement> extends DocumentedMultiCanvasFrame implements TopLevelFrame<ELEMENT>{
protected final InteractionManager editor;
protected final EntityResolver projectResolver;
private final String stylePrefix;
@OnThread(value = Tag.Any,requireSynchronized = true)
protected ELEMENT element;
protected FrameContentRow packageRow;
protected SlotLabel packageNameLabel;
protected final FrameContentRow importRow;
protected final FrameCanvas importCanvas;
protected final ObservableList<String> boundImports = FXCollections.observableArrayList();
protected final SlotLabel importsLabel;
protected final TriangleLabel importTriangleLabel;
protected TextSlot<NameDefSlotFragment> paramName;
protected final FrameCanvas fieldsCanvas;
protected final FrameCanvas methodsCanvas;
protected final SlotLabel fieldsLabel;
protected final SlotLabel methodsLabel;
protected final FrameContentRow fieldsLabelRow;
protected final FrameContentRow methodsLabelRow;
protected final FrameContentItem endSpacer;
public TopLevelDocumentMultiCanvasFrame(InteractionManager editor, EntityResolver projectResolver, String caption,
String stylePrefix, String packageName, List<ImportElement> imports,
JavadocUnit documentation, NameDefSlotFragment topLevelFrameName, boolean enabled)
{
super(editor, caption, stylePrefix);
this.editor = editor;
this.projectResolver = projectResolver;
this.stylePrefix = stylePrefix;
endSpacer = new FrameContentItem() {
private Rectangle r = new Rectangle(1, 200, Color.TRANSPARENT);
@Override
public Stream getHeaderItemsDeep()
{
return Stream.empty();
}
@Override
public Stream getHeaderItemsDirect()
{
return Stream.empty();
}
@Override
public Bounds getSceneBounds()
{
return r.localToScene(r.getBoundsInLocal());
}
@Override
public Optional getCanvas()
{
return Optional.empty();
}
@Override
public boolean focusLeftEndFromPrev()
{
return false;
}
@Override
public boolean focusRightEndFromNext()
{
return false;
}
@Override
public boolean focusTopEndFromPrev()
{
return false;
}
@Override
public boolean focusBottomEndFromNext()
{
return false;
}
@Override
public void setView(View oldView, View newView, SharedTransition animation)
{
}
@Override
public Node getNode()
{
return r;
}
};
if (Config.isGreenfoot())
{
this.packageRow = null;
this.packageNameLabel = null;
}
else
{
if (packageName != null && !packageName.isEmpty()) {
this.packageRow = new FrameContentRow(this);
this.packageNameLabel = new SlotLabel(packageName, "package-slot-");
this.packageRow.bindContentsConcat(FXCollections.<ObservableList<? extends HeaderItem>>observableArrayList(
FXCollections.observableArrayList(new SlotLabel("package "), this.packageNameLabel)
));
}
}
importsLabel = makeLabel(Config.getString("frame.editor.label.imports"));
fieldsLabel = makeLabel(Config.getString("frame.editor.label.fields"));
methodsLabel = makeLabel(Config.getString("frame.editor.label.methods"));
importCanvas = createImportsCanvas(imports);
importCanvas.getShowingProperty().set(false);
JavaFXUtil.addChangeListener(importCanvas.getShowingProperty(), showing -> {
if (!showing && isCanvasHasFocus(importCanvas)) {
getfieldsCanvas().getFirstCursor().requestFocus();
}
});
importTriangleLabel = new TriangleLabel(editor, t -> importCanvas.growUsing(t.getProgress()),
t -> importCanvas.shrinkUsing(t.getOppositeProgress()), importCanvas.getShowingProperty());
JavaFXUtil.addChangeListenerPlatform(importTriangleLabel.expandedProperty(),
b -> editor.updateErrorOverviewBar());
importRow = new FrameContentRow(this, importsLabel, importTriangleLabel);
Properties localProperties = new Properties();
localProperties.put("CAPTION", caption);
paramName = new ClassNameDefTextSlot(editor, this, getHeaderRow(), stylePrefix + "name-");
paramName.addValueListener(SlotTraversalChars.IDENTIFIER);
paramName.setPromptText(Config.getString("frame.editor.param.prompt", null, localProperties));
paramName.setText(topLevelFrameName);
localProperties.put("CLASSNAME", paramName.textProperty().get());
setDocumentation(documentation.toString());
documentationPromptTextProperty().bind(new SimpleStringProperty(
Config.getString("frame.editor.toplevel.doc.prompt", null, localProperties)));
this.fieldsCanvas = new FrameCanvas(editor, this, stylePrefix + "fields-");
fieldsLabelRow = new FrameContentRow(this, fieldsLabel);
addCanvas(fieldsLabelRow, fieldsCanvas);
this.methodsCanvas = new FrameCanvas(editor, this, stylePrefix);
methodsLabelRow = new FrameContentRow(this, methodsLabel);
addCanvas(methodsLabelRow, methodsCanvas);
frameEnabledProperty.set(enabled);
}
| Returns true if the focus inside a canvas, this is one of two cases:
| 1- One of the FrameCursors inside the canvas is focused
| 2- One of the focusable slots in one of the frames inside the canvas is focused
|
| @param canvas the FrameCanvas we are looking into
| @return True only if one of focuasble targets inside the canvas is focused.
|
protected boolean isCanvasHasFocus(FrameCanvas canvas)
{
if (canvas.getFocusableCursors().stream().anyMatch(c -> c.isFocused()) ) {
return true;
}
if (canvas.getBlockContents().stream().anyMatch(b -> b.getFocusablesInclContained().anyMatch(s -> s.isFocused()))) {
return true;
}
return false;
}
protected SlotLabel makeLabel(String content)
{
SlotLabel l = new SlotLabel(content);
JavaFXUtil.addStyleClass(l, stylePrefix + "section-label");
return l;
}
@Override
public boolean canDrag()
{
return false;
}
protected List getMembers(FrameCanvas frameCanvas)
{
List<CodeElement> members = new ArrayList<>();
for (CodeFrame<?> c : frameCanvas.getBlocksSubtype(CodeFrame.class)) {
c.regenerateCode();
members.add(c.getCode());
}
return members;
}
private FrameCanvas createImportsCanvas(final List<ImportElement> imports)
{
FrameCanvas importCanvas = new FrameCanvas(editor, new CanvasParent() {
@Override
public FrameCursor findCursor(double sceneX, double sceneY, FrameCursor prevCursor, FrameCursor nextCursor, List<Frame> exclude, boolean isDrag, boolean canDescend)
{
return TopLevelDocumentMultiCanvasFrame.this.importCanvas.findClosestCursor(sceneX, sceneY, exclude, isDrag, canDescend);
}
@Override
public FrameTypeCheck check(FrameCanvas canvasBase)
{
return StrideDictionary.checkImport();
}
@Override
public List getAvailableExtensions(FrameCanvas canvas, FrameCursor cursor)
{
return Collections.emptyList();
}
@Override
public Frame getFrame()
{
return TopLevelDocumentMultiCanvasFrame.this;
}
@Override
public InteractionManager getEditor()
{
return editor;
}
@Override
public CanvasKind getChildKind(FrameCanvas c)
{
return CanvasKind.IMPORTS;
}
}, stylePrefix + "import-");
importCanvas.setAnimateLeftMarginScale(true);
List<ImportElement> importsRev = new ArrayList<>(imports);
Collections.reverse(importsRev);
importsRev.forEach(item -> importCanvas.insertBlockBefore(item.createFrame(editor), importCanvas.getFirstCursor()));
JavaFXUtil.onceInScene(importCanvas.getNode(), () -> importCanvas.shrinkUsing(new ReadOnlyDoubleWrapper(0.0)));
new DeepListBinding<String>(boundImports) {
private final ChangeListener<String> listener = (a, b, c) -> update();
private final MultiListener<ObservableStringValue> stringListener
= new MultiListener<>(v -> { v.addListener(listener); return () -> v.removeListener(listener);
});
@Override
protected Stream> getListenTargets()
{
return Stream.of(importCanvas.getBlockContents());
}
@Override
protected Stream calculateValues()
{
return importCanvas.getBlockContents().stream().map(f -> (ImportFrame)f).map(ImportFrame::getImport);
}
@Override
protected void update()
{
stringListener.listenOnlyTo(importCanvas.getBlockContents().stream().map(f -> (ImportFrame)f).map(ImportFrame::importProperty));
super.update();
}
}.startListening();
return importCanvas;
}
public ObservableList getImports()
{
return boundImports;
}
public void addImport(String importSrc)
{
importCanvas.insertBlockAfter(new ImportFrame(editor, importSrc), importCanvas.getLastCursor());
}
public FrameCanvas getfieldsCanvas()
{
return fieldsCanvas;
}
public FrameCanvas getMethodsCanvas()
{
return methodsCanvas;
}
@Override
protected void modifyChildren(List<FrameContentItem> updatedChildren)
{
super.modifyChildren(updatedChildren);
int n = 0;
if (packageNameLabel != null)
{
updatedChildren.add(n, packageRow);
n += 1;
}
updatedChildren.add(n, importRow);
updatedChildren.add(n+1, importCanvas);
updatedChildren.add(endSpacer);
}
@Override
public void bindMinHeight(DoubleBinding prop)
{
getRegion().minHeightProperty().bind(prop);
}
@Override
public void insertAtEnd(Frame frame)
{
getLastCanvas().getLastCursor().insertBlockAfter(frame);
}
@Override
public ObservableStringValue nameProperty()
{
return paramName.textProperty();
}
@Override
public FrameCanvas getImportCanvas()
{
return importCanvas;
}
@Override
public void ensureImportCanvasShowing()
{
importCanvas.getShowingProperty().set(true);
}
@Override
public EditableSlot getErrorShowRedirect()
{
return paramName;
}
@Override
public void focusName()
{
paramName.requestFocus(Focus.LEFT);
}
@Override
public Stream getFocusables()
{
return getFocusablesInclContained();
}
@Override
public void focusOnBody(BodyFocus on)
{
FrameCursor c;
if (on == BodyFocus.TOP)
{
c = fieldsCanvas.getFirstCursor();
}
else if (on == BodyFocus.BOTTOM)
{
c = methodsCanvas.getLastCursor();
}
else
{
Optional<CodeError> error = getCurrentErrors().findFirst();
if (error.isPresent())
{
error.get().jumpTo(editor);
return;
}
Frame specialMethod = findASpecialMethod();
if (specialMethod != null)
{
c = specialMethod.getFirstInternalCursor();
}
else
{
c = methodsCanvas.getFirstCursor();
}
}
c.requestFocus();
editor.scrollTo(c.getNode(), -100);
}
abstract protected Frame findASpecialMethod();
@Override
@OnThread(Tag.FXPlatform)
public void setView(View oldView, View newView, SharedTransition animateProgress)
{
super.setView(oldView, newView, animateProgress);
boolean java = newView == View.JAVA_PREVIEW;
if (java || oldView == View.JAVA_PREVIEW) {
fieldsCanvas.previewCurly(java, true, false, header.getLeftFirstItem(), null, animateProgress);
methodsCanvas.previewCurly(java, false, true, header.getLeftFirstItem(), null, animateProgress);
}
getCanvases().forEach(canvas -> {
canvas.setView(oldView, newView, animateProgress);
canvas.getCursors().forEach(c -> c.setView(newView, animateProgress));
});
animateLabelRows(newView, animateProgress);
if (java)
importTriangleLabel.expandedProperty().set(true);
else if (newView.isBirdseye())
importTriangleLabel.expandedProperty().set(false);
animateCanvasLabels(oldView, newView, animateProgress);
}
private void animateLabelRows(View newView, SharedTransition animateProgress)
{
final List<FrameContentRow> labelRows = getLabelRows();
if (newView == View.NORMAL)
{
animateProgress.addOnStopped(() -> {
importTriangleLabel.setVisible(true);
importTriangleLabel.setManaged(true);
labelRows.forEach(r -> r.setSnapToPixel(true));
});
}
else
{
labelRows.forEach(r -> r.setSnapToPixel(false));
importTriangleLabel.setVisible(false);
importTriangleLabel.setManaged(false);
}
}
protected abstract List getLabelRows();
private void animateCanvasLabels(View oldView, View newView, SharedTransition animateProgress)
{
List<SlotLabel> animateLabels = getCanvasLabels();
if (newView == View.JAVA_PREVIEW)
{
animateLabels.forEach(l -> l.shrinkVertically(animateProgress));
}
else if (oldView == View.JAVA_PREVIEW)
{
animateLabels.forEach(l -> l.growVertically(animateProgress));
}
}
protected abstract List getCanvasLabels();
}
. TopLevelDocumentMultiCanvasFrame
. getHeaderItemsDeep
. getHeaderItemsDirect
. getSceneBounds
. getCanvas
. focusLeftEndFromPrev
. focusRightEndFromNext
. focusTopEndFromPrev
. focusBottomEndFromNext
. setView
. getNode
. HeaderItem>>observableArrayList
. isCanvasHasFocus
. makeLabel
. canDrag
. getMembers
. createImportsCanvas
. findCursor
. check
. getAvailableExtensions
. getFrame
. getEditor
. getChildKind
. getListenTargets
. calculateValues
. update
. getImports
. addImport
. getfieldsCanvas
. getMethodsCanvas
. modifyChildren
. bindMinHeight
. insertAtEnd
. nameProperty
. getImportCanvas
. ensureImportCanvasShowing
. getErrorShowRedirect
. focusName
. getFocusables
. focusOnBody
. findASpecialMethod
. setView
. animateLabelRows
. getLabelRows
. animateCanvasLabels
. getCanvasLabels
642 neLoCode
+ 7 LoComm