editor adjustments
This commit is contained in:
parent
ba55b8e65f
commit
7bc5c5ba11
@ -78,12 +78,12 @@ public enum I18n {
|
||||
CODE_EDITOR_NAVIGATOR_DETAIL("codeEditor.navigator.detail"),
|
||||
CODE_EDITOR_OUTLINE_TITLE("codeEditor.outline.title"),
|
||||
CODE_EDITOR_OUTLINE_PLACEHOLDER("codeEditor.outline.placeholder"),
|
||||
CODE_EDITOR_OUTLINE_DIAGNOSTICS("codeEditor.outline.diagnostics"),
|
||||
CODE_EDITOR_OUTLINE_SYMBOLS("codeEditor.outline.symbols"),
|
||||
CODE_EDITOR_OUTLINE_EMPTY_DIAGNOSTICS("codeEditor.outline.emptyDiagnostics"),
|
||||
CODE_EDITOR_OUTLINE_EMPTY_SYMBOLS("codeEditor.outline.emptySymbols"),
|
||||
CODE_EDITOR_HELPER_TITLE("codeEditor.helper.title"),
|
||||
CODE_EDITOR_HELPER_PLACEHOLDER("codeEditor.helper.placeholder"),
|
||||
CODE_EDITOR_HELPER_DIAGNOSTICS("codeEditor.helper.diagnostics"),
|
||||
CODE_EDITOR_HELPER_EMPTY_DIAGNOSTICS("codeEditor.helper.emptyDiagnostics"),
|
||||
CODE_EDITOR_TABS_PLACEHOLDER("codeEditor.tabs.placeholder"),
|
||||
CODE_EDITOR_TABS_OVERFLOW("codeEditor.tabs.overflow"),
|
||||
CODE_EDITOR_STATUS_BREADCRUMB("codeEditor.status.breadcrumb"),
|
||||
@ -91,8 +91,6 @@ public enum I18n {
|
||||
CODE_EDITOR_STATUS_LINE_SEPARATOR("codeEditor.status.lineSeparator"),
|
||||
CODE_EDITOR_STATUS_INDENTATION("codeEditor.status.indentation"),
|
||||
CODE_EDITOR_STATUS_LANGUAGE("codeEditor.status.language"),
|
||||
CODE_EDITOR_STATUS_EDITABLE("codeEditor.status.editable"),
|
||||
CODE_EDITOR_STATUS_READ_ONLY("codeEditor.status.readOnly"),
|
||||
CODE_EDITOR_COMMAND_SAVE("codeEditor.command.save"),
|
||||
CODE_EDITOR_COMMAND_SAVE_ALL("codeEditor.command.saveAll"),
|
||||
CODE_EDITOR_WARNING_FRONTEND_READ_ONLY("codeEditor.warning.frontendReadOnly"),
|
||||
|
||||
@ -2,15 +2,23 @@ package p.studio.workspaces.editor;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import p.studio.Container;
|
||||
import p.studio.controls.WorkspaceDockPane;
|
||||
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public final class EditorHelperPanel extends WorkspaceDockPane {
|
||||
public static final double COLLAPSED_HEIGHT = 34.0;
|
||||
public static final double MINIMUM_EXPANDED_HEIGHT = 120.0;
|
||||
public static final double DEFAULT_EXPANDED_HEIGHT = 180.0;
|
||||
private final Label summary = new Label();
|
||||
private final Label diagnosticsTitle = new Label();
|
||||
private final VBox diagnosticsBox = new VBox(6);
|
||||
|
||||
public EditorHelperPanel() {
|
||||
super(
|
||||
@ -22,14 +30,67 @@ public final class EditorHelperPanel extends WorkspaceDockPane {
|
||||
"editor-workspace-helper-pane",
|
||||
"editor-workspace-helper-panel");
|
||||
|
||||
final var placeholder = new Label();
|
||||
placeholder.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_HELPER_PLACEHOLDER));
|
||||
placeholder.getStyleClass().add("editor-workspace-placeholder");
|
||||
placeholder.setWrapText(true);
|
||||
summary.getStyleClass().addAll("editor-workspace-placeholder", "editor-workspace-helper-summary");
|
||||
summary.setWrapText(true);
|
||||
diagnosticsTitle.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_HELPER_DIAGNOSTICS));
|
||||
diagnosticsTitle.getStyleClass().add("editor-workspace-outline-section-title");
|
||||
diagnosticsBox.getStyleClass().add("editor-workspace-outline-list");
|
||||
|
||||
final var content = new VBox(8, placeholder);
|
||||
final var content = new VBox(10, summary, diagnosticsTitle, diagnosticsBox);
|
||||
content.getStyleClass().add("editor-workspace-panel-content");
|
||||
content.setPadding(new Insets(10, 16, 14, 16));
|
||||
setDockContent(content);
|
||||
final var scrollPane = new ScrollPane(content);
|
||||
scrollPane.setFitToWidth(true);
|
||||
scrollPane.getStyleClass().add("editor-workspace-outline-scroll");
|
||||
setDockContent(scrollPane);
|
||||
showPlaceholder();
|
||||
}
|
||||
|
||||
public void showPlaceholder() {
|
||||
summary.textProperty().unbind();
|
||||
summary.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_HELPER_PLACEHOLDER));
|
||||
diagnosticsBox.getChildren().setAll(placeholderLabel(I18n.CODE_EDITOR_HELPER_EMPTY_DIAGNOSTICS));
|
||||
}
|
||||
|
||||
public void showSemanticReadResult(
|
||||
final Path documentPath,
|
||||
final List<LspDiagnosticDTO> diagnostics) {
|
||||
summary.textProperty().unbind();
|
||||
summary.setText(documentPath.getFileName() + " • semantic read-only");
|
||||
rebuildDiagnostics(diagnostics);
|
||||
}
|
||||
|
||||
private void rebuildDiagnostics(final List<LspDiagnosticDTO> diagnostics) {
|
||||
diagnosticsBox.getChildren().clear();
|
||||
if (diagnostics.isEmpty()) {
|
||||
diagnosticsBox.getChildren().add(placeholderLabel(I18n.CODE_EDITOR_HELPER_EMPTY_DIAGNOSTICS));
|
||||
return;
|
||||
}
|
||||
for (final LspDiagnosticDTO diagnostic : diagnostics) {
|
||||
final var label = new Label(formatDiagnostic(diagnostic));
|
||||
label.setWrapText(true);
|
||||
label.getStyleClass().addAll(
|
||||
"editor-workspace-outline-item",
|
||||
diagnostic.severity().name().equals("ERROR")
|
||||
? "editor-workspace-outline-diagnostic-error"
|
||||
: "editor-workspace-outline-diagnostic-warning");
|
||||
diagnosticsBox.getChildren().add(label);
|
||||
}
|
||||
}
|
||||
|
||||
private Label placeholderLabel(final I18n key) {
|
||||
final var label = new Label();
|
||||
label.textProperty().bind(Container.i18n().bind(key));
|
||||
label.setWrapText(true);
|
||||
label.getStyleClass().addAll("editor-workspace-placeholder", "editor-workspace-outline-item");
|
||||
return label;
|
||||
}
|
||||
|
||||
private String formatDiagnostic(final LspDiagnosticDTO diagnostic) {
|
||||
return "%s [%d,%d) %s".formatted(
|
||||
diagnostic.severity().name(),
|
||||
diagnostic.range().startOffset(),
|
||||
diagnostic.range().endOffset(),
|
||||
diagnostic.message());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,6 @@ import javafx.geometry.Insets;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ScrollPane;
|
||||
import javafx.scene.layout.VBox;
|
||||
import p.studio.lsp.dtos.LspDiagnosticDTO;
|
||||
import p.studio.lsp.dtos.LspSymbolDTO;
|
||||
import p.studio.Container;
|
||||
import p.studio.controls.WorkspaceDockPane;
|
||||
@ -18,7 +17,6 @@ public final class EditorOutlinePanel extends WorkspaceDockPane {
|
||||
private static final double MINIMUM_EXPANDED_HEIGHT = 120.0;
|
||||
private static final double DEFAULT_HEIGHT = 180.0;
|
||||
private final Label summary = new Label();
|
||||
private final VBox diagnosticsBox = new VBox(6);
|
||||
private final VBox symbolsBox = new VBox(6);
|
||||
|
||||
public EditorOutlinePanel() {
|
||||
@ -33,16 +31,12 @@ public final class EditorOutlinePanel extends WorkspaceDockPane {
|
||||
summary.getStyleClass().addAll("editor-workspace-placeholder", "editor-workspace-outline-summary");
|
||||
summary.setWrapText(true);
|
||||
|
||||
final var diagnosticsTitle = sectionTitle(I18n.CODE_EDITOR_OUTLINE_DIAGNOSTICS);
|
||||
final var symbolsTitle = sectionTitle(I18n.CODE_EDITOR_OUTLINE_SYMBOLS);
|
||||
|
||||
diagnosticsBox.getStyleClass().add("editor-workspace-outline-list");
|
||||
symbolsBox.getStyleClass().add("editor-workspace-outline-list");
|
||||
|
||||
final var content = new VBox(10,
|
||||
summary,
|
||||
diagnosticsTitle,
|
||||
diagnosticsBox,
|
||||
symbolsTitle,
|
||||
symbolsBox);
|
||||
content.getStyleClass().add("editor-workspace-panel-content");
|
||||
@ -57,38 +51,17 @@ public final class EditorOutlinePanel extends WorkspaceDockPane {
|
||||
public void showPlaceholder() {
|
||||
summary.textProperty().unbind();
|
||||
summary.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_OUTLINE_PLACEHOLDER));
|
||||
diagnosticsBox.getChildren().setAll(placeholderLabel(I18n.CODE_EDITOR_OUTLINE_EMPTY_DIAGNOSTICS));
|
||||
symbolsBox.getChildren().setAll(placeholderLabel(I18n.CODE_EDITOR_OUTLINE_EMPTY_SYMBOLS));
|
||||
}
|
||||
|
||||
public void showSemanticReadResult(
|
||||
final Path documentPath,
|
||||
final List<LspDiagnosticDTO> diagnostics,
|
||||
final List<LspSymbolDTO> symbols) {
|
||||
summary.textProperty().unbind();
|
||||
summary.setText(documentPath.getFileName() + " • semantic read-only");
|
||||
rebuildDiagnostics(diagnostics);
|
||||
rebuildSymbols(symbols);
|
||||
}
|
||||
|
||||
private void rebuildDiagnostics(final List<LspDiagnosticDTO> diagnostics) {
|
||||
diagnosticsBox.getChildren().clear();
|
||||
if (diagnostics.isEmpty()) {
|
||||
diagnosticsBox.getChildren().add(placeholderLabel(I18n.CODE_EDITOR_OUTLINE_EMPTY_DIAGNOSTICS));
|
||||
return;
|
||||
}
|
||||
for (final LspDiagnosticDTO diagnostic : diagnostics) {
|
||||
final var label = new Label(formatDiagnostic(diagnostic));
|
||||
label.setWrapText(true);
|
||||
label.getStyleClass().addAll(
|
||||
"editor-workspace-outline-item",
|
||||
diagnostic.severity().name().equals("ERROR")
|
||||
? "editor-workspace-outline-diagnostic-error"
|
||||
: "editor-workspace-outline-diagnostic-warning");
|
||||
diagnosticsBox.getChildren().add(label);
|
||||
}
|
||||
}
|
||||
|
||||
private void rebuildSymbols(final List<LspSymbolDTO> symbols) {
|
||||
symbolsBox.getChildren().clear();
|
||||
if (symbols.isEmpty()) {
|
||||
@ -125,12 +98,4 @@ public final class EditorOutlinePanel extends WorkspaceDockPane {
|
||||
label.getStyleClass().addAll("editor-workspace-placeholder", "editor-workspace-outline-item");
|
||||
return label;
|
||||
}
|
||||
|
||||
private String formatDiagnostic(final LspDiagnosticDTO diagnostic) {
|
||||
return "%s [%d,%d) %s".formatted(
|
||||
diagnostic.severity().name(),
|
||||
diagnostic.range().startOffset(),
|
||||
diagnostic.range().endOffset(),
|
||||
diagnostic.message());
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,6 +6,7 @@ import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import p.studio.Container;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
@ -19,7 +20,7 @@ public final class EditorStatusBar extends HBox {
|
||||
private final Label lineSeparator = new Label();
|
||||
private final Label indentation = new Label();
|
||||
private final Label language = new Label();
|
||||
private final Label readOnly = new Label();
|
||||
private final StackPane accessMode = new StackPane();
|
||||
|
||||
public EditorStatusBar() {
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
@ -40,8 +41,7 @@ public final class EditorStatusBar extends HBox {
|
||||
indentation.getStyleClass().add("editor-workspace-status-chip-indentation");
|
||||
styleChip(language);
|
||||
language.getStyleClass().add("editor-workspace-status-chip-language");
|
||||
styleChip(readOnly);
|
||||
readOnly.getStyleClass().add("editor-workspace-status-chip-read-only");
|
||||
accessMode.getStyleClass().addAll("editor-workspace-status-chip", "editor-workspace-status-chip-access-mode");
|
||||
|
||||
getChildren().addAll(
|
||||
breadcrumb,
|
||||
@ -50,7 +50,7 @@ public final class EditorStatusBar extends HBox {
|
||||
lineSeparator,
|
||||
indentation,
|
||||
language,
|
||||
readOnly
|
||||
accessMode
|
||||
);
|
||||
|
||||
}
|
||||
@ -66,7 +66,7 @@ public final class EditorStatusBar extends HBox {
|
||||
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
|
||||
setText(language, fileBuffer.typeId());
|
||||
EditorDocumentPresentationStyles.applyToStatusChip(language, presentation);
|
||||
showAccessMode(fileBuffer);
|
||||
showAccessMode(fileBuffer.readOnly());
|
||||
}
|
||||
|
||||
public void showPlaceholder(final EditorDocumentPresentation presentation) {
|
||||
@ -77,11 +77,8 @@ public final class EditorStatusBar extends HBox {
|
||||
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
|
||||
bindDefault(language, I18n.CODE_EDITOR_STATUS_LANGUAGE);
|
||||
EditorDocumentPresentationStyles.applyToStatusChip(language, presentation);
|
||||
bindDefault(readOnly, I18n.CODE_EDITOR_STATUS_READ_ONLY);
|
||||
readOnly.getStyleClass().remove("editor-workspace-status-chip-editable");
|
||||
if (!readOnly.getStyleClass().contains("editor-workspace-status-chip-read-only")) {
|
||||
readOnly.getStyleClass().add("editor-workspace-status-chip-read-only");
|
||||
}
|
||||
accessMode.getChildren().clear();
|
||||
accessMode.getStyleClass().removeAll("editor-workspace-status-chip-editable", "editor-workspace-status-chip-read-only");
|
||||
}
|
||||
|
||||
private void bindDefault(final Label label, final I18n key) {
|
||||
@ -175,29 +172,22 @@ public final class EditorStatusBar extends HBox {
|
||||
setVisibleManaged(lineSeparator, visible);
|
||||
setVisibleManaged(indentation, visible);
|
||||
setVisibleManaged(language, visible);
|
||||
setVisibleManaged(readOnly, visible);
|
||||
setVisibleManaged(accessMode, visible);
|
||||
}
|
||||
|
||||
private void showAccessMode(final EditorOpenFileBuffer fileBuffer) {
|
||||
if (fileBuffer.readOnly()) {
|
||||
bindDefault(readOnly, I18n.CODE_EDITOR_STATUS_READ_ONLY);
|
||||
readOnly.getStyleClass().remove("editor-workspace-status-chip-editable");
|
||||
if (!readOnly.getStyleClass().contains("editor-workspace-status-chip-read-only")) {
|
||||
readOnly.getStyleClass().add("editor-workspace-status-chip-read-only");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
bindDefault(readOnly, I18n.CODE_EDITOR_STATUS_EDITABLE);
|
||||
readOnly.getStyleClass().remove("editor-workspace-status-chip-read-only");
|
||||
if (!readOnly.getStyleClass().contains("editor-workspace-status-chip-editable")) {
|
||||
readOnly.getStyleClass().add("editor-workspace-status-chip-editable");
|
||||
}
|
||||
private void showAccessMode(final boolean readOnlyMode) {
|
||||
accessMode.getChildren().setAll(readOnlyMode
|
||||
? EditorWorkspaceIcons.lockClosed()
|
||||
: EditorWorkspaceIcons.lockOpen());
|
||||
accessMode.getStyleClass().removeAll("editor-workspace-status-chip-editable", "editor-workspace-status-chip-read-only");
|
||||
accessMode.getStyleClass().add(readOnlyMode
|
||||
? "editor-workspace-status-chip-read-only"
|
||||
: "editor-workspace-status-chip-editable");
|
||||
}
|
||||
|
||||
private void setVisibleManaged(final Label label, final boolean visible) {
|
||||
label.setVisible(visible);
|
||||
label.setManaged(visible);
|
||||
private void setVisibleManaged(final Node node, final boolean visible) {
|
||||
node.setVisible(visible);
|
||||
node.setManaged(visible);
|
||||
}
|
||||
|
||||
private void styleChip(final Label label) {
|
||||
|
||||
@ -0,0 +1,103 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.Region;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class EditorWarningStrip extends HBox {
|
||||
private final Label message = new Label();
|
||||
private final Label counter = new Label();
|
||||
private final Button previousButton = new Button("‹");
|
||||
private final Button nextButton = new Button("›");
|
||||
private final Button closeButton = new Button("×");
|
||||
|
||||
private List<WarningItem> warnings = List.of();
|
||||
private int activeIndex;
|
||||
private boolean dismissed;
|
||||
|
||||
public EditorWarningStrip() {
|
||||
setAlignment(Pos.CENTER_LEFT);
|
||||
getStyleClass().add("editor-workspace-warning");
|
||||
|
||||
message.setWrapText(true);
|
||||
message.setMaxWidth(Double.MAX_VALUE);
|
||||
HBox.setHgrow(message, Priority.ALWAYS);
|
||||
|
||||
counter.getStyleClass().add("editor-workspace-warning-counter");
|
||||
|
||||
previousButton.getStyleClass().add("editor-workspace-warning-button");
|
||||
nextButton.getStyleClass().add("editor-workspace-warning-button");
|
||||
closeButton.getStyleClass().add("editor-workspace-warning-button");
|
||||
|
||||
previousButton.setFocusTraversable(false);
|
||||
nextButton.setFocusTraversable(false);
|
||||
closeButton.setFocusTraversable(false);
|
||||
|
||||
previousButton.setOnAction(event -> showIndex(activeIndex - 1));
|
||||
nextButton.setOnAction(event -> showIndex(activeIndex + 1));
|
||||
closeButton.setOnAction(event -> dismiss());
|
||||
|
||||
final var spacer = new Region();
|
||||
HBox.setHgrow(spacer, Priority.ALWAYS);
|
||||
getChildren().addAll(message, spacer, counter, previousButton, nextButton, closeButton);
|
||||
setVisible(false);
|
||||
setManaged(false);
|
||||
}
|
||||
|
||||
public void showWarnings(final List<WarningItem> warningItems) {
|
||||
warnings = List.copyOf(Objects.requireNonNull(warningItems, "warningItems"));
|
||||
activeIndex = 0;
|
||||
dismissed = false;
|
||||
refresh();
|
||||
}
|
||||
|
||||
public void clearWarnings() {
|
||||
warnings = List.of();
|
||||
activeIndex = 0;
|
||||
dismissed = false;
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void dismiss() {
|
||||
dismissed = true;
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void showIndex(final int candidateIndex) {
|
||||
if (warnings.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
activeIndex = Math.floorMod(candidateIndex, warnings.size());
|
||||
refresh();
|
||||
}
|
||||
|
||||
private void refresh() {
|
||||
final boolean visible = !dismissed && !warnings.isEmpty();
|
||||
setVisible(visible);
|
||||
setManaged(visible);
|
||||
if (!visible) {
|
||||
message.setText("");
|
||||
counter.setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
final WarningItem warning = warnings.get(activeIndex);
|
||||
message.setText(warning.message());
|
||||
counter.setText(warnings.size() == 1 ? "1/1" : (activeIndex + 1) + "/" + warnings.size());
|
||||
final boolean canNavigate = warnings.size() > 1;
|
||||
previousButton.setDisable(!canNavigate);
|
||||
nextButton.setDisable(!canNavigate);
|
||||
}
|
||||
|
||||
public record WarningItem(String message) {
|
||||
public WarningItem {
|
||||
message = Objects.requireNonNull(message, "message");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,12 @@
|
||||
package p.studio.workspaces.editor;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.SplitPane;
|
||||
import javafx.scene.layout.*;
|
||||
import org.fxmisc.flowless.VirtualizedScrollPane;
|
||||
import org.fxmisc.richtext.CodeArea;
|
||||
import org.fxmisc.richtext.LineNumberFactory;
|
||||
import p.studio.lsp.LspService;
|
||||
@ -27,9 +28,10 @@ import java.util.Objects;
|
||||
public final class EditorWorkspace extends Workspace {
|
||||
private final BorderPane root = new BorderPane();
|
||||
private final CodeArea codeArea = new CodeArea();
|
||||
private final VirtualizedScrollPane<CodeArea> codeScroller = new VirtualizedScrollPane<>(codeArea);
|
||||
private final Button saveButton = new Button();
|
||||
private final Button saveAllButton = new Button();
|
||||
private final Label readOnlyWarning = new Label();
|
||||
private final EditorWarningStrip warningStrip = new EditorWarningStrip();
|
||||
private final EditorProjectNavigatorPanel navigatorPanel = new EditorProjectNavigatorPanel();
|
||||
private final EditorOutlinePanel outlinePanel = new EditorOutlinePanel();
|
||||
private final EditorHelperPanel helperPanel = new EditorHelperPanel();
|
||||
@ -135,9 +137,19 @@ public final class EditorWorkspace extends Workspace {
|
||||
try {
|
||||
codeArea.replaceText(fileBuffer.content());
|
||||
codeArea.setStyleSpans(0, highlighting.styleSpans());
|
||||
codeArea.moveTo(0);
|
||||
codeArea.requestFollowCaret();
|
||||
} finally {
|
||||
syncingEditor = false;
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
codeArea.moveTo(0);
|
||||
codeArea.showParagraphAtTop(0);
|
||||
codeArea.requestFollowCaret();
|
||||
if (fileBuffer.editable()) {
|
||||
codeArea.requestFocus();
|
||||
}
|
||||
});
|
||||
codeArea.setEditable(fileBuffer.editable());
|
||||
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation);
|
||||
refreshCommandSurfaces(fileBuffer);
|
||||
@ -164,25 +176,28 @@ public final class EditorWorkspace extends Workspace {
|
||||
|
||||
private void showEditorPlaceholder() {
|
||||
final EditorDocumentPresentation presentation = presentationRegistry.resolve("text");
|
||||
final String placeholder = """
|
||||
// Controlled write wave
|
||||
// Open a supported project document from the navigator.
|
||||
""";
|
||||
applyPresentationStylesheets(presentation);
|
||||
syncingEditor = true;
|
||||
try {
|
||||
codeArea.replaceText(placeholder);
|
||||
codeArea.setStyleSpans(0, presentation.highlight(placeholder));
|
||||
codeArea.replaceText("");
|
||||
codeArea.setStyleSpans(0, presentation.highlight(""));
|
||||
codeArea.moveTo(0);
|
||||
codeArea.requestFollowCaret();
|
||||
} finally {
|
||||
syncingEditor = false;
|
||||
}
|
||||
Platform.runLater(() -> {
|
||||
codeArea.moveTo(0);
|
||||
codeArea.showParagraphAtTop(0);
|
||||
codeArea.requestFollowCaret();
|
||||
});
|
||||
codeArea.setEditable(false);
|
||||
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation);
|
||||
saveButton.setDisable(true);
|
||||
saveAllButton.setDisable(true);
|
||||
readOnlyWarning.setVisible(false);
|
||||
readOnlyWarning.setManaged(false);
|
||||
warningStrip.clearWarnings();
|
||||
outlinePanel.showPlaceholder();
|
||||
helperPanel.showPlaceholder();
|
||||
}
|
||||
|
||||
private void applyPresentationStylesheets(final EditorDocumentPresentation presentation) {
|
||||
@ -210,9 +225,13 @@ public final class EditorWorkspace extends Workspace {
|
||||
}
|
||||
|
||||
private VBox buildCenterColumn() {
|
||||
final var centerColumn = new VBox(12, buildCommandBar(), readOnlyWarning, tabStrip, codeArea);
|
||||
final var editorSurface = new VBox(0, warningStrip, codeScroller);
|
||||
editorSurface.getStyleClass().add("editor-workspace-editor-surface");
|
||||
codeScroller.getStyleClass().add("editor-workspace-code-scroller");
|
||||
VBox.setVgrow(codeScroller, Priority.ALWAYS);
|
||||
final var centerColumn = new VBox(12, buildCommandBar(), tabStrip, editorSurface);
|
||||
centerColumn.getStyleClass().add("editor-workspace-center-column");
|
||||
VBox.setVgrow(codeArea, Priority.ALWAYS);
|
||||
VBox.setVgrow(editorSurface, Priority.ALWAYS);
|
||||
return centerColumn;
|
||||
}
|
||||
|
||||
@ -234,11 +253,7 @@ public final class EditorWorkspace extends Workspace {
|
||||
}
|
||||
|
||||
private void configureWarning() {
|
||||
readOnlyWarning.textProperty().bind(p.studio.Container.i18n().bind(I18n.CODE_EDITOR_WARNING_FRONTEND_READ_ONLY));
|
||||
readOnlyWarning.getStyleClass().add("editor-workspace-warning");
|
||||
readOnlyWarning.setWrapText(true);
|
||||
readOnlyWarning.setVisible(false);
|
||||
readOnlyWarning.setManaged(false);
|
||||
warningStrip.clearWarnings();
|
||||
}
|
||||
|
||||
private HBox buildCommandBar() {
|
||||
@ -302,9 +317,16 @@ public final class EditorWorkspace extends Workspace {
|
||||
private void refreshCommandSurfaces(final EditorOpenFileBuffer fileBuffer) {
|
||||
saveButton.setDisable(!fileBuffer.saveEnabled());
|
||||
saveAllButton.setDisable(!openFileSession.hasDirtyEditableFiles());
|
||||
final boolean showWarning = fileBuffer.frontendDocument() && fileBuffer.readOnly();
|
||||
readOnlyWarning.setVisible(showWarning);
|
||||
readOnlyWarning.setManaged(showWarning);
|
||||
final List<EditorWarningStrip.WarningItem> warnings = new ArrayList<>();
|
||||
if (fileBuffer.frontendDocument() && fileBuffer.readOnly()) {
|
||||
warnings.add(new EditorWarningStrip.WarningItem(
|
||||
p.studio.Container.i18n().text(I18n.CODE_EDITOR_WARNING_FRONTEND_READ_ONLY)));
|
||||
}
|
||||
if (warnings.isEmpty()) {
|
||||
warningStrip.clearWarnings();
|
||||
return;
|
||||
}
|
||||
warningStrip.showWarnings(warnings);
|
||||
}
|
||||
|
||||
private EditorOpenFileBuffer bufferFrom(final VfsDocumentOpenResult.VfsTextDocument textDocument) {
|
||||
@ -324,11 +346,10 @@ public final class EditorWorkspace extends Workspace {
|
||||
final LspAnalyzeDocumentResult analysis) {
|
||||
if (!fileBuffer.frontendDocument() || analysis == null) {
|
||||
outlinePanel.showPlaceholder();
|
||||
helperPanel.showPlaceholder();
|
||||
return;
|
||||
}
|
||||
outlinePanel.showSemanticReadResult(
|
||||
fileBuffer.path(),
|
||||
analysis.diagnostics(),
|
||||
analysis.documentSymbols());
|
||||
outlinePanel.showSemanticReadResult(fileBuffer.path(), analysis.documentSymbols());
|
||||
helperPanel.showSemanticReadResult(fileBuffer.path(), analysis.diagnostics());
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,14 @@ import javafx.scene.Node;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.shape.SVGPath;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public final class EditorWorkspaceIcons {
|
||||
private static final Pattern SVG_PATH_PATTERN = Pattern.compile("<path[^>]*\\sd=\"([^\"]+)\"");
|
||||
private static final String FOLDER_PATH = "M1.5 4.5A1.5 1.5 0 0 1 3 3h3.2l1.1 1.4H13A1.5 1.5 0 0 1 14.5 5.9V12A1.5 1.5 0 0 1 13 13.5H3A1.5 1.5 0 0 1 1.5 12z";
|
||||
private static final String FILE_PATH = "M4 1.5h5l3 3V13A1.5 1.5 0 0 1 10.5 14.5h-6A1.5 1.5 0 0 1 3 13V3A1.5 1.5 0 0 1 4.5 1.5zm4.7 1.2V5h2.3z";
|
||||
private static final String COG_PATH = "M9.405 1.05c-.413-1.4-2.397-1.4-2.81 0l-.1.34a1.464 1.464 0 0 1-2.105.872l-.31-.17c-1.24-.68-2.64.72-1.96 1.96l.17.31c.446.816.023 1.84-.872 2.105l-.34.1c-1.4.413-1.4 2.397 0 2.81l.34.1c.895.265 1.318 1.289.872 2.105l-.17.31c-.68 1.24.72 2.64 1.96 1.96l.31-.17a1.464 1.464 0 0 1 2.105.872l.1.34c.413 1.4 2.397 1.4 2.81 0l.1-.34a1.464 1.464 0 0 1 2.105-.872l.31.17c1.24.68 2.64-.72 1.96-1.96l-.17-.31a1.464 1.464 0 0 1 .872-2.105l.34-.1c1.4-.413 1.4-2.397 0-2.81l-.34-.1a1.464 1.464 0 0 1-.872-2.105l.17-.31c.68-1.24-.72-2.64-1.96-1.96l-.31.17a1.464 1.464 0 0 1-2.105-.872z";
|
||||
@ -42,6 +49,14 @@ public final class EditorWorkspaceIcons {
|
||||
return icon(TARGET_PATH, "editor-workspace-icon-target");
|
||||
}
|
||||
|
||||
public static Node lockClosed() {
|
||||
return icon(iconPathFromResource("/icons/editor/lock-closed.svg"), "editor-workspace-icon-lock-closed");
|
||||
}
|
||||
|
||||
public static Node lockOpen() {
|
||||
return icon(iconPathFromResource("/icons/editor/lock-open.svg"), "editor-workspace-icon-lock-open");
|
||||
}
|
||||
|
||||
private static Node icon(final String path, final String toneClass) {
|
||||
final var shape = new SVGPath();
|
||||
shape.setContent(path);
|
||||
@ -51,4 +66,20 @@ public final class EditorWorkspaceIcons {
|
||||
icon.getStyleClass().add("editor-workspace-icon");
|
||||
return icon;
|
||||
}
|
||||
|
||||
private static String iconPathFromResource(final String resourcePath) {
|
||||
try (InputStream inputStream = EditorWorkspaceIcons.class.getResourceAsStream(resourcePath)) {
|
||||
if (inputStream == null) {
|
||||
throw new IllegalStateException("Missing icon resource: " + resourcePath);
|
||||
}
|
||||
final String svg = new String(inputStream.readAllBytes(), StandardCharsets.UTF_8);
|
||||
final Matcher matcher = SVG_PATH_PATTERN.matcher(svg);
|
||||
if (!matcher.find()) {
|
||||
throw new IllegalStateException("Missing <path d=\"...\"> in icon resource: " + resourcePath);
|
||||
}
|
||||
return matcher.group(1);
|
||||
} catch (IOException exception) {
|
||||
throw new IllegalStateException("Unable to read icon resource: " + resourcePath, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -68,13 +68,13 @@ codeEditor.navigator.revealActive=Reveal active file
|
||||
codeEditor.navigator.placeholder=Project navigation lands in the next implementation slice.
|
||||
codeEditor.navigator.detail=This first shell reserves the full navigator surface, its refresh action, and the left-column composition without wiring project-tree data yet.
|
||||
codeEditor.outline.title=Outline
|
||||
codeEditor.outline.placeholder=Open a frontend document to inspect read-only diagnostics and symbols.
|
||||
codeEditor.outline.diagnostics=Diagnostics
|
||||
codeEditor.outline.placeholder=Open a frontend document to inspect semantic symbols.
|
||||
codeEditor.outline.symbols=Symbols
|
||||
codeEditor.outline.emptyDiagnostics=No diagnostics for the active frontend document.
|
||||
codeEditor.outline.emptySymbols=No semantic symbols are currently available for the active frontend document.
|
||||
codeEditor.helper.title=Editor Helper
|
||||
codeEditor.helper.placeholder=This region is intentionally passive in the first read-only wave.
|
||||
codeEditor.helper.placeholder=Open a frontend document to inspect semantic diagnostics.
|
||||
codeEditor.helper.diagnostics=Diagnostics
|
||||
codeEditor.helper.emptyDiagnostics=No diagnostics for the active frontend document.
|
||||
codeEditor.tabs.placeholder=no-file-open.txt
|
||||
codeEditor.tabs.overflow=More
|
||||
codeEditor.status.breadcrumb=proj > src > file.pbs
|
||||
@ -82,8 +82,6 @@ codeEditor.status.position=L:C
|
||||
codeEditor.status.lineSeparator=LF
|
||||
codeEditor.status.indentation=Spaces: 4
|
||||
codeEditor.status.language=Text
|
||||
codeEditor.status.editable=Editable
|
||||
codeEditor.status.readOnly=Read-only
|
||||
codeEditor.command.save=Save
|
||||
codeEditor.command.saveAll=Save All
|
||||
codeEditor.warning.frontendReadOnly=This frontend file is read-only in this wave. It cannot be edited or saved yet.
|
||||
|
||||
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M5 6V4.75a3 3 0 1 1 6 0V6h.75A1.25 1.25 0 0 1 13 7.25v5.5A1.25 1.25 0 0 1 11.75 14h-7.5A1.25 1.25 0 0 1 3 12.75v-5.5A1.25 1.25 0 0 1 4.25 6zm1.5 0h3V4.75a1.5 1.5 0 1 0-3 0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 255 B |
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path d="M3.2 10.667c0-4.137.11-4.267 3.609-4.267 2.758 0 3.746-.523 4.189-2.218.766-2.928 5.375-2.44 5.834.618.32 2.131.318 2.131-1.365.005-1.445-1.825-1.826-1.901-2.675-.533-.618.996-.603 1.728.042 1.95.568.196 1.032 2.236 1.032 4.534v4.178H8.533H3.2z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 327 B |
@ -506,6 +506,14 @@
|
||||
-fx-fill: #eff5fb;
|
||||
}
|
||||
|
||||
.editor-workspace-icon-lock-closed {
|
||||
-fx-fill: #f6d78f;
|
||||
}
|
||||
|
||||
.editor-workspace-icon-lock-open {
|
||||
-fx-fill: #bde7c7;
|
||||
}
|
||||
|
||||
.editor-workspace-outline-panel {
|
||||
-fx-min-height: 0;
|
||||
}
|
||||
@ -686,6 +694,16 @@
|
||||
-fx-fill: #f2f6fb;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area .caret {
|
||||
-fx-stroke: #ffd27a;
|
||||
}
|
||||
|
||||
.editor-workspace-editor-surface {
|
||||
-fx-background-color: #171c22;
|
||||
-fx-border-color: #2a313c;
|
||||
-fx-border-width: 1;
|
||||
}
|
||||
|
||||
.editor-workspace-code-area-type-text .text {
|
||||
-fx-fill: #eef3f8;
|
||||
}
|
||||
@ -707,13 +725,43 @@
|
||||
}
|
||||
|
||||
.editor-workspace-warning {
|
||||
-fx-alignment: center-left;
|
||||
-fx-spacing: 8;
|
||||
-fx-padding: 8 12 8 12;
|
||||
-fx-background-color: #3b2a10;
|
||||
-fx-border-color: #8f6730;
|
||||
-fx-border-width: 0 0 1 0;
|
||||
-fx-text-fill: #f7ddb0;
|
||||
-fx-font-size: 12px;
|
||||
}
|
||||
|
||||
.editor-workspace-warning-counter {
|
||||
-fx-text-fill: #f3d8a7;
|
||||
-fx-font-size: 11px;
|
||||
-fx-font-weight: 700;
|
||||
}
|
||||
|
||||
.editor-workspace-warning-button {
|
||||
-fx-min-width: 24;
|
||||
-fx-pref-width: 24;
|
||||
-fx-max-width: 24;
|
||||
-fx-min-height: 24;
|
||||
-fx-pref-height: 24;
|
||||
-fx-max-height: 24;
|
||||
-fx-padding: 0;
|
||||
-fx-background-color: #4b3413;
|
||||
-fx-border-color: #9a7440;
|
||||
-fx-text-fill: #f7ddb0;
|
||||
-fx-font-size: 11px;
|
||||
-fx-background-radius: 4;
|
||||
-fx-border-radius: 4;
|
||||
}
|
||||
|
||||
.editor-workspace-warning-button:hover {
|
||||
-fx-background-color: #5a4018;
|
||||
-fx-border-color: #b38749;
|
||||
}
|
||||
|
||||
.workspace-dock-pane {
|
||||
-fx-collapsible: true;
|
||||
-fx-background-color: transparent;
|
||||
@ -753,6 +801,10 @@
|
||||
-fx-min-height: 0;
|
||||
}
|
||||
|
||||
.editor-workspace-helper-summary {
|
||||
-fx-text-fill: #dce6f0;
|
||||
}
|
||||
|
||||
.workspace-dock-pane-collapsed {
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
@ -858,10 +910,17 @@
|
||||
-fx-max-width: 72;
|
||||
}
|
||||
|
||||
.editor-workspace-status-chip-access-mode {
|
||||
-fx-min-width: 36;
|
||||
-fx-pref-width: 36;
|
||||
-fx-max-width: 36;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.editor-workspace-status-chip-read-only {
|
||||
-fx-min-width: 88;
|
||||
-fx-pref-width: 88;
|
||||
-fx-max-width: 88;
|
||||
-fx-background-color: #3b2a10;
|
||||
-fx-border-color: #8f6730;
|
||||
-fx-text-fill: #f7ddb0;
|
||||
}
|
||||
|
||||
.editor-workspace-status-chip-editable {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user