package bluej.stride.framedjava.frames;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

import bluej.stride.framedjava.slots.TypeSlot;
import bluej.stride.generic.ExtensionDescription.ExtensionSource;
import bluej.stride.generic.FrameCursor;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.util.Duration;

import bluej.stride.framedjava.ast.AccessPermission;
import bluej.stride.framedjava.ast.AccessPermissionFragment;
import bluej.stride.framedjava.ast.FilledExpressionSlotFragment;
import bluej.stride.framedjava.ast.NameDefSlotFragment;
import bluej.stride.framedjava.ast.TypeSlotFragment;
import bluej.stride.framedjava.elements.CodeElement;
import bluej.stride.framedjava.elements.VarElement;
import bluej.stride.framedjava.slots.ExpressionSlot;
import bluej.stride.framedjava.slots.FilledExpressionSlot;
import bluej.stride.generic.CanvasParent;
import bluej.stride.generic.ExtensionDescription;
import bluej.stride.generic.Frame;
import bluej.stride.generic.FrameCanvas;
import bluej.stride.generic.FrameFactory;
import bluej.stride.generic.InteractionManager;
import bluej.stride.generic.SingleLineFrame;
import bluej.stride.operations.FrameOperation;
import bluej.stride.operations.ToggleBooleanProperty;
import bluej.stride.slots.AccessPermissionSlot;
import bluej.stride.slots.EditableSlot;
import bluej.stride.slots.ChoiceSlot;
import bluej.stride.slots.Focus;
import bluej.stride.slots.FocusParent;
import bluej.stride.slots.HeaderItem;
import bluej.stride.slots.SlotLabel;
import bluej.stride.slots.SlotTraversalChars;
import bluej.stride.slots.SlotValueListener;
import bluej.stride.slots.VariableNameDefTextSlot;

import bluej.utility.javafx.FXRunnable;
import bluej.utility.javafx.JavaFXUtil;
import bluej.utility.javafx.SharedTransition;
import threadchecker.OnThread;
import threadchecker.Tag;


| A variable/object declaration block (with optional init) | @author Fraser McKay | public class VarFrame extends SingleLineFrame implements CodeFrame<VarElement>, DebuggableFrame{ private static final String STATIC_NAME = "static"; private static final String FINAL_NAME = "final"; private static final String TOGGLE_STATIC_VAR = "toggleStaticVar"; private static final String TOGGLE_FINAL_VAR = "toggleFinalVar"; private final BooleanProperty accessModifier = new SimpleBooleanProperty(false); private final ChoiceSlot<AccessPermission> access; private final SlotLabel staticLabel = new SlotLabel(STATIC_NAME + " "); private final BooleanProperty staticModifier = new SimpleBooleanProperty(false); private final SlotLabel finalLabel = new SlotLabel(FINAL_NAME + " "); private final BooleanProperty finalModifier = new SimpleBooleanProperty(false); private final TypeSlot slotType; private final VariableNameDefTextSlot slotName; private final BooleanProperty showingValue = new SimpleBooleanProperty(false); private final SlotLabel assignLabel = new SlotLabel(AssignFrame.ASSIGN_SYMBOL); private final ExpressionSlot<FilledExpressionSlotFragment> slotValue; private final BooleanProperty slotValueBlank = new SimpleBooleanProperty(true); private VarElement element; private final BooleanProperty hasKeyboardFocus = new SimpleBooleanProperty(false); private final BooleanProperty inInterfaceProperty = new SimpleBooleanProperty(false);
| Default constructor. | @param editor | VarFrame(final InteractionManager editor, boolean isFinal, boolean isStatic) { super(editor, "var ", "var-"); staticModifier.set(isStatic); finalModifier.set(isFinal); modifiers.put(STATIC_NAME, staticModifier); modifiers.put(FINAL_NAME, finalModifier); headerCaptionLabel.setAnimateCaption(false); slotName = new VariableNameDefTextSlot(editor, this, getHeaderRow(), () -> isField(getParentCanvas()), "var-name-"); slotName.addValueListener(new SlotValueListener() { @Override public boolean valueChanged(HeaderItem slot, String oldValue, String newValue, FocusParent<HeaderItem> parent) { return true; } @Override @OnThread(Tag.FXPlatform) public void deletePressedAtEnd(HeaderItem slot) { deleteAtEnd(getHeaderRow(), slot); } @Override @OnThread(Tag.FXPlatform) public void backSpacePressedAtStart(HeaderItem slot) { backspaceAtStart(getHeaderRow(), slot); } }); slotName.setPromptText("name"); slotType = new TypeSlot(editor, this, this, getHeaderRow(), TypeSlot.Role.DECLARATION, "var-type-"); slotType.setSimplePromptText("type"); slotType.addClosingChar(' '); access = new AccessPermissionSlot(editor, this, getHeaderRow(), "var-access-"); access.setValue(AccessPermission.PRIVATE); slotValue = new FilledExpressionSlot(editor, this, this, getHeaderRow(), "var-value-"); slotValue.bindTargetType(slotType.javaProperty()); slotValue.setSimplePromptText("value"); slotValue.onLostFocus(this::checkForEmptySlot); JavaFXUtil.addChangeListener(showingValue, showing -> { if (!showing) { slotValue.cleanup(); } }); FXRunnable runAddValSlot = () -> { getHeaderRow().focusRight(slotName); }; slotName.addValueListener(new SlotTraversalChars(runAddValSlot, SlotTraversalChars.ASSIGN_LHS.getChars())); getHeaderRow().bindContentsConcat(FXCollections.<ObservableList<? extends HeaderItem>>observableArrayList( FXCollections.observableArrayList(headerCaptionLabel), JavaFXUtil.listBool(accessModifier, access), JavaFXUtil.listBool(staticModifier, staticLabel), JavaFXUtil.listBool(finalModifier, finalLabel), FXCollections.observableArrayList(slotType, slotName), JavaFXUtil.listBool(showingValue, List.of(assignLabel, slotValue)), FXCollections.observableArrayList(previewSemi) )); JavaFXUtil.addChangeListener(staticModifier, b -> editor.modifiedFrame(this, false)); JavaFXUtil.addChangeListener(finalModifier, b -> editor.modifiedFrame(this, false)); hasKeyboardFocus.bind( (accessModifier.and(access.effectivelyFocusedProperty())) .or(slotType.effectivelyFocusedProperty()) .or(slotName.effectivelyFocusedProperty()) .or(slotValue.effectivelyFocusedProperty()) ); slotValue.onTextPropertyChange(s -> slotValueBlank.set(s.isEmpty())); ReadOnlyBooleanProperty keyFocusDelayed = JavaFXUtil.delay(hasKeyboardFocus, Duration.ZERO, Duration.millis(100)); showingValue.bind(inInterfaceProperty.or(keyFocusDelayed).or(slotValueBlank.not())); } public VarFrame(InteractionManager editor, AccessPermissionFragment accessValue, boolean staticModifier, boolean finalModifier, TypeSlotFragment varType, NameDefSlotFragment varName, FilledExpressionSlotFragment varValue, boolean enabled) { this(editor, finalModifier, staticModifier); accessModifier.set(accessValue != null); if (accessValue != null) { access.setValue(accessValue.getValue()); } slotType.setText(varType); slotName.setText(varName); if (varValue != null) { slotValue.setText(varValue); } frameEnabledProperty.set(enabled); } @Override public void regenerateCode() { final boolean generateValue = showingValue.get() && (!slotValue.getText().isEmpty() || slotValue.isCurrentlyCompleting()); element = new VarElement(this, accessModifier.get() ? new AccessPermissionFragment(access.getValue(AccessPermission.EMPTY)) : null, staticModifier.get(), finalModifier.get(), slotType.getSlotElement(), slotName.getSlotElement(), generateValue ? slotValue.getSlotElement() : null, frameEnabledProperty.get()); } @Override public VarElement getCode() { return element; } public static FrameFactory getFactory() { return new FrameFactory<VarFrame>() { @Override public VarFrame createBlock(InteractionManager editor) { return new VarFrame(editor, false, false); } @Override public Class getBlockClass() { return VarFrame.class; } }; } public static FrameFactory getLocalConstantFactory() { return new FrameFactory<VarFrame>() { @Override public VarFrame createBlock(InteractionManager editor) { return new VarFrame(editor, true, false); } @Override public Class getBlockClass() { return VarFrame.class; } }; } public static FrameFactory getClassConstantFactory() { return new FrameFactory<VarFrame>() { @Override public VarFrame createBlock(InteractionManager editor) { return new VarFrame(editor, true, true); } @Override public Class getBlockClass() { return VarFrame.class; } }; } public static FrameFactory getInterfaceConstantFactory() { return new FrameFactory<VarFrame>() { @Override public VarFrame createBlock(InteractionManager editor) { return new VarFrame(editor, false, false); } @Override public Class getBlockClass() { return VarFrame.class; } }; } @Override public void updateAppearance(FrameCanvas parentCanvas) { super.updateAppearance(parentCanvas); if (parentCanvas == null) { return; } inInterfaceProperty.setValue(isInInterface(parentCanvas)); if (isField(parentCanvas)) { if (inInterfaceProperty.getValue()) { addStyleClass("interface-var-frame"); } else { accessModifier.set(true); addStyleClass("class-var-frame"); } headerCaptionLabel.setText(""); } else { staticModifier.set(false); accessModifier.set(false); removeStyleClass(isInInterface(parentCanvas) ? "interface-var-frame" : "class-var-frame"); headerCaptionLabel.setText("var "); JavaFXUtil.setPseudoclass("bj-transparent", isAfterVarFrame(parentCanvas), headerCaptionLabel.getNode()); } } private boolean isAfterVarFrame(FrameCanvas parentCanvas) { Frame frameBefore = parentCanvas.getFrameBefore(getCursorBefore()); int counter = 0; while ( frameBefore != null && !frameBefore.isEffectiveFrame() && counter < 2){ counter++; frameBefore = parentCanvas.getFrameBefore(frameBefore.getCursorBefore()); } return frameBefore instanceof VarFrame; } private boolean isField(FrameCanvas parentCanvas) { if (parentCanvas == null) { return false; } return parentCanvas.getParent().getChildKind(parentCanvas) == CanvasParent.CanvasKind.FIELDS; } @Override @OnThread(Tag.FXPlatform) public List getContextOperations() { List<FrameOperation> operations = new ArrayList<>(super.getContextOperations()); operations.addAll(getStaticFinalOperations()); return operations; } @Override public List getDeclaredVariablesAfter() { String name = slotName.getText(); if (name.isEmpty()) { return Collections.emptyList(); } return Arrays.asList(name); } @Override public EditableSlot getErrorShowRedirect() { return slotName; } @Override public void focusName() { slotName.requestFocus(Focus.LEFT); } @Override public void setView(View oldView, View newView, SharedTransition animateProgress) { super.setViewNoOverride(oldView, newView, animateProgress); if (newView == View.JAVA_PREVIEW) headerCaptionLabel.shrinkHorizontally(animateProgress); else{ headerCaptionLabel.growHorizontally(animateProgress); } assignLabel.setText(newView == View.JAVA_PREVIEW ? "=" : AssignFrame.ASSIGN_SYMBOL); } @Override public boolean tryRestoreTo(CodeElement codeElement) { if (codeElement instanceof VarElement) { VarElement nme = (VarElement)codeElement; staticModifier.set(nme.isStatic()); finalModifier.set(nme.isFinal()); slotType.setText(nme.getType()); slotName.setText(nme.getName()); if (nme.getValue() != null) { slotValue.setText(nme.getValue()); } else if (slotValue != null) { slotValue.setText(""); } return true; } return false; } @Override public boolean focusWhenJustAdded() { slotType.requestFocus(); return true; } @Override public List getAvailableExtensions(FrameCanvas innerCanvas, FrameCursor cursorInCanvas) { final List<ExtensionDescription> extensions = new ArrayList<>(super.getAvailableExtensions(innerCanvas, cursorInCanvas)); if ( !inInterfaceProperty.getValue() ) { getStaticFinalOperations().forEach(op -> extensions.add(new ExtensionDescription(op, this, true, ExtensionSource.BEFORE, ExtensionSource.AFTER, ExtensionSource.MODIFIER, ExtensionSource.SELECTION))); } return extensions; } private List getStaticFinalOperations() { List<ToggleBooleanProperty> operations = new ArrayList<>(); operations.add(new ToggleBooleanProperty(getEditor(), TOGGLE_FINAL_VAR, FINAL_NAME, 'n')); if (isField(getParentCanvas())) { operations.add(new ToggleBooleanProperty(getEditor(), TOGGLE_STATIC_VAR, STATIC_NAME, 's')); } return operations; } @Override public Stream getPossiblyHiddenSlotsDirect() { return Stream.of(access, slotValue); } }

.   valueChanged
.   deletePressedAtEnd
.   backSpacePressedAtStart
.   HeaderItem>>observableArrayList
.   VarFrame
.   regenerateCode
.   getCode
.   getFactory
.   createBlock
.   getBlockClass
.   getLocalConstantFactory
.   createBlock
.   getBlockClass
.   getClassConstantFactory
.   createBlock
.   getBlockClass
.   getInterfaceConstantFactory
.   createBlock
.   getBlockClass
.   updateAppearance
.   isAfterVarFrame
.   isField
.   getContextOperations
.   getDeclaredVariablesAfter
.   getErrorShowRedirect
.   focusName
.   setView
.   tryRestoreTo
.   focusWhenJustAdded
.   getAvailableExtensions
.   getStaticFinalOperations
.   getPossiblyHiddenSlotsDirect




541 neLoCode + 4 LoComm