Skip to content

Commit

Permalink
Add menu showing all problems for ProblemOverlay when indicator is cl…
Browse files Browse the repository at this point in the history
…icked
  • Loading branch information
Col-E committed Sep 19, 2023
1 parent ea1063f commit fdd7837
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public void clear() {
* @param listener
* Listener to add.
*/
public void addListener(ProblemInvalidationListener listener) {
public void addListener(@Nonnull ProblemInvalidationListener listener) {
listeners.add(listener);
}

Expand All @@ -110,7 +110,7 @@ public void addListener(ProblemInvalidationListener listener) {
* @return {@code true} when listener was removed.
* {@code false} when listener was not present to begin with.
*/
public boolean removeListener(ProblemInvalidationListener listener) {
public boolean removeListener(@Nonnull ProblemInvalidationListener listener) {
return listeners.remove(listener);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,27 @@
package software.coley.recaf.ui.pane.editing;

import atlantafx.base.controls.Popover;
import atlantafx.base.theme.Styles;
import jakarta.annotation.Nonnull;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ScrollPane;
import javafx.scene.effect.BoxBlur;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import org.fxmisc.richtext.CodeArea;
import org.kordamp.ikonli.carbonicons.CarbonIcons;
Expand All @@ -22,10 +30,13 @@
import software.coley.recaf.ui.control.richtext.Editor;
import software.coley.recaf.ui.control.richtext.EditorComponent;
import software.coley.recaf.ui.control.richtext.ScrollbarPaddingUtil;
import software.coley.recaf.ui.control.richtext.problem.Problem;
import software.coley.recaf.ui.control.richtext.problem.ProblemInvalidationListener;
import software.coley.recaf.ui.control.richtext.problem.ProblemLevel;
import software.coley.recaf.ui.control.richtext.problem.ProblemTracking;

import java.util.Collection;

/**
* Simple problem overlay, showing users how many problems of what type there are in the current {@link Editor}.
*
Expand All @@ -41,13 +52,84 @@ public class ProblemOverlay extends Group implements EditorComponent, ProblemInv
*/
public ProblemOverlay() {
// Display the number of problems, and their type.
Button indicator = new Button();
indicator.setFocusTraversable(false);
indicator.getStyleClass().addAll(Styles.SMALL, "muted");

// On-click to show a list of all problems
indicator.setOnAction(e -> {
ProblemTracking problemTracking = editor.getProblemTracking();
if (problemTracking == null) return;

// Create vertical list
VBox content = new VBox();
ObservableList<Node> children = content.getChildren();
Collection<Problem> problems = problemTracking.getProblems().values();
for (Problem problem : problems) {
// Map level to graphic
ProblemLevel level = problem.getLevel();
Node graphic = switch (level) {
case ERROR -> new FontIconView(CarbonIcons.ERROR, Color.RED);
case WARN -> new FontIconView(CarbonIcons.WARNING_ALT, Color.YELLOW);
default -> new FontIconView(CarbonIcons.INFORMATION, Color.TURQUOISE);
};

// Create 'N: Message' layout
// - Exclude line number 'N' when line number is negative
Label messageLabel = new Label(problem.getMessage());
messageLabel.setTextFill(Color.RED);
messageLabel.setMaxWidth(Integer.MAX_VALUE);
int line = problem.getLine();
HBox problemBox;
if (line >= 0) {
Label lineLabel = new Label(String.valueOf(line), graphic);
lineLabel.setTextFill(Color.RED);
lineLabel.getStyleClass().add(Styles.TEXT_BOLD);
problemBox = new HBox(lineLabel, messageLabel);
} else {
messageLabel.setGraphic(graphic);
problemBox = new HBox(messageLabel);
}
problemBox.setSpacing(5);
problemBox.setPadding(new Insets(5));

// Make on-hover more clearly show which problem is relevant.
// The changing color on-hover also indicates clickable action.
problemBox.setOnMouseEntered(me -> problemBox.getStyleClass().add("background"));
problemBox.setOnMouseExited(me -> problemBox.getStyleClass().remove("background"));

// When clicked, center the relevant problem.
problemBox.setOnMousePressed(me -> {
CodeArea codeArea = editor.getCodeArea();
codeArea.moveTo(line - 1, 0);
codeArea.selectLine();
codeArea.showParagraphAtCenter(codeArea.getCurrentParagraph());
});
children.add(problemBox);
}

BooleanProperty isMouseOver = new SimpleBooleanProperty(true);
ScrollPane scrollWrapper = new ScrollPane(content);
scrollWrapper.maxHeightProperty().bind(editor.heightProperty().multiply(0.8));
scrollWrapper.prefViewportWidthProperty().bind(editor.widthProperty().multiply(0.8));
scrollWrapper.setOnMouseEntered(me -> isMouseOver.set(true));
scrollWrapper.setOnMouseExited(me -> isMouseOver.set(false));
scrollWrapper.effectProperty().bind(Bindings.when(isMouseOver.not())
.then(new BoxBlur(5, 5, 1))
.otherwise((BoxBlur) null));
Popover popover = new Popover(scrollWrapper);
popover.setArrowLocation(Popover.ArrowLocation.TOP_RIGHT);
popover.opacityProperty().bind(Bindings.when(isMouseOver)
.then(1.0)
.otherwise(0.4));
popover.show(indicator);
});

// Can recycle the same instance with the indicator graphic
FontIconView iconGood = new FontIconView(CarbonIcons.CHECKMARK, Color.LAWNGREEN);
FontIconView iconInfo = new FontIconView(CarbonIcons.INFORMATION, Color.TURQUOISE);
FontIconView iconWarning = new FontIconView(CarbonIcons.WARNING_ALT, Color.YELLOW);
FontIconView iconError = new FontIconView(CarbonIcons.ERROR, Color.RED);
Button indicator = new Button();
indicator.setFocusTraversable(false);
indicator.getStyleClass().addAll(Styles.SMALL, "muted");
indicator.graphicProperty().bind(problemCount.map(size -> {
// Skip before linked to an editor.
if (editor == null)
Expand All @@ -73,6 +155,7 @@ public ProblemOverlay() {
if (error > 0) wrapper.getChildren().add(new Label(String.valueOf(error), iconError));
return wrapper;
}));

BooleanBinding hasProblems = problemCount.greaterThan(0);
hasProblems.addListener((ob, had, has) -> {
// When there are problems, this is the left-most button.
Expand Down Expand Up @@ -124,7 +207,7 @@ public ProblemOverlay() {
}
});
prev.getStyleClass().addAll(Styles.BUTTON_ICON, Styles.CENTER_PILL, Styles.SMALL, "muted");
next.getStyleClass().addAll(Styles.BUTTON_ICON, Styles.RIGHT_PILL, Styles.SMALL, "muted");
next.getStyleClass().addAll(Styles.BUTTON_ICON, Styles.RIGHT_PILL, Styles.SMALL, "muted");
prev.setFocusTraversable(false);
next.setFocusTraversable(false);
HBox buttons = new HBox(prev, next);
Expand Down
3 changes: 3 additions & 0 deletions recaf-ui/src/main/resources/style/tweaks.css
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
.background {
-fx-background-color: -color-bg-default;
}
.background-light {
-fx-background-color: -color-border-subtle;
}

/* Rounds the display of a container by its bg/border colors */
.round-container {
Expand Down

0 comments on commit fdd7837

Please sign in to comment.