editor status bar with behaviour

This commit is contained in:
bQUARKz 2026-04-04 09:46:46 +01:00
parent eb2520fa40
commit e82d3e90b4
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 98 additions and 11 deletions

View File

@ -101,7 +101,7 @@
} }
.editor-workspace-status-chip-type-pbs { .editor-workspace-status-chip-type-pbs {
-fx-background-color: #1e2d3b; -fx-background-color: #2c2140;
-fx-border-color: #4aa3ff; -fx-border-color: #a47dff;
-fx-text-fill: #dce9f8; -fx-text-fill: #efe6ff;
} }

View File

@ -15,6 +15,7 @@ import java.nio.file.Path;
import java.util.Optional; import java.util.Optional;
public final class EditorStatusBar extends HBox { public final class EditorStatusBar extends HBox {
private static final String DEFAULT_INDENTATION = "Spaces: 4";
private final HBox breadcrumb = new HBox(6); private final HBox breadcrumb = new HBox(6);
private final Label position = new Label(); private final Label position = new Label();
private final Label lineSeparator = new Label(); private final Label lineSeparator = new Label();
@ -61,9 +62,8 @@ public final class EditorStatusBar extends HBox {
final EditorDocumentPresentation presentation) { final EditorDocumentPresentation presentation) {
showBreadcrumb(projectReference, fileBuffer.path()); showBreadcrumb(projectReference, fileBuffer.path());
showMetadata(true); showMetadata(true);
bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION); showPosition(fileBuffer.editable(), 1, 1);
setText(lineSeparator, fileBuffer.lineSeparator()); showDocumentFormatting(fileBuffer.lineSeparator(), fileBuffer.content());
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
setText(language, fileBuffer.typeId()); setText(language, fileBuffer.typeId());
EditorDocumentPresentationStyles.applyToStatusChip(language, presentation); EditorDocumentPresentationStyles.applyToStatusChip(language, presentation);
showAccessMode(fileBuffer.readOnly()); showAccessMode(fileBuffer.readOnly());
@ -72,7 +72,7 @@ public final class EditorStatusBar extends HBox {
public void showPlaceholder(final EditorDocumentPresentation presentation) { public void showPlaceholder(final EditorDocumentPresentation presentation) {
breadcrumb.getChildren().clear(); breadcrumb.getChildren().clear();
showMetadata(false); showMetadata(false);
bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION); showPosition(false, 1, 1);
bindDefault(lineSeparator, I18n.CODE_EDITOR_STATUS_LINE_SEPARATOR); bindDefault(lineSeparator, I18n.CODE_EDITOR_STATUS_LINE_SEPARATOR);
bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION); bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION);
bindDefault(language, I18n.CODE_EDITOR_STATUS_LANGUAGE); bindDefault(language, I18n.CODE_EDITOR_STATUS_LANGUAGE);
@ -81,6 +81,18 @@ public final class EditorStatusBar extends HBox {
accessMode.getStyleClass().removeAll("editor-workspace-status-chip-editable", "editor-workspace-status-chip-read-only"); accessMode.getStyleClass().removeAll("editor-workspace-status-chip-editable", "editor-workspace-status-chip-read-only");
} }
public void showCaretPosition(final int line, final int column) {
if (!position.isManaged()) {
return;
}
setText(position, line + ":" + column);
}
public void showDocumentFormatting(final String separator, final String content) {
setText(lineSeparator, separator);
setText(indentation, detectIndentation(content));
}
private void bindDefault(final Label label, final I18n key) { private void bindDefault(final Label label, final I18n key) {
bindText(label, key); bindText(label, key);
} }
@ -168,13 +180,21 @@ public final class EditorStatusBar extends HBox {
} }
private void showMetadata(final boolean visible) { private void showMetadata(final boolean visible) {
setVisibleManaged(position, visible);
setVisibleManaged(lineSeparator, visible); setVisibleManaged(lineSeparator, visible);
setVisibleManaged(indentation, visible); setVisibleManaged(indentation, visible);
setVisibleManaged(language, visible); setVisibleManaged(language, visible);
setVisibleManaged(accessMode, visible); setVisibleManaged(accessMode, visible);
} }
private void showPosition(final boolean visible, final int line, final int column) {
setVisibleManaged(position, visible);
if (!visible) {
bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION);
return;
}
setText(position, line + ":" + column);
}
private void showAccessMode(final boolean readOnlyMode) { private void showAccessMode(final boolean readOnlyMode) {
accessMode.getChildren().setAll(readOnlyMode accessMode.getChildren().setAll(readOnlyMode
? EditorWorkspaceIcons.lockClosed() ? EditorWorkspaceIcons.lockClosed()
@ -195,4 +215,58 @@ public final class EditorStatusBar extends HBox {
label.getStyleClass().add("editor-workspace-status-chip"); label.getStyleClass().add("editor-workspace-status-chip");
} }
} }
private String detectIndentation(final String content) {
final String normalized = content == null ? "" : content.replace("\r\n", "\n").replace('\r', '\n');
int spaceGcd = 0;
boolean sawTabs = false;
for (final String line : normalized.split("\n", -1)) {
if (line.isBlank()) {
continue;
}
int spaces = 0;
int tabs = 0;
while (spaces + tabs < line.length()) {
final char ch = line.charAt(spaces + tabs);
if (ch == ' ') {
if (tabs > 0) {
spaces = 0;
break;
}
spaces++;
continue;
}
if (ch == '\t') {
tabs++;
continue;
}
break;
}
if (tabs > 0 && spaces == 0) {
sawTabs = true;
continue;
}
if (spaces > 1) {
spaceGcd = spaceGcd == 0 ? spaces : gcd(spaceGcd, spaces);
}
}
if (sawTabs) {
return "Tabs: 1";
}
if (spaceGcd > 0) {
return "Spaces: " + spaceGcd;
}
return DEFAULT_INDENTATION;
}
private int gcd(final int left, final int right) {
int a = Math.abs(left);
int b = Math.abs(right);
while (b != 0) {
final int next = a % b;
a = b;
b = next;
}
return a == 0 ? 1 : a;
}
} }

View File

@ -75,8 +75,11 @@ public final class EditorWorkspace extends Workspace {
codeArea.setEditable(false); codeArea.setEditable(false);
codeArea.setWrapText(false); codeArea.setWrapText(false);
codeArea.textProperty().addListener((ignored, previous, current) -> syncActiveDocumentToVfs(current)); codeArea.textProperty().addListener((ignored, previous, current) -> syncActiveDocumentToVfs(current));
codeArea.caretPositionProperty().addListener((ignored, previous, current) -> codeArea.caretPositionProperty().addListener((ignored, previous, current) -> {
updateActiveGuides(current == null ? 0 : current.intValue())); final int caretOffset = current == null ? 0 : current.intValue();
updateActiveGuides(caretOffset);
refreshStatusBarCaret();
});
codeArea.getStyleClass().add("editor-workspace-code-area"); codeArea.getStyleClass().add("editor-workspace-code-area");
codeArea.addEventFilter(KeyEvent.KEY_PRESSED, this::guardInlineHintMutation); codeArea.addEventFilter(KeyEvent.KEY_PRESSED, this::guardInlineHintMutation);
codeArea.addEventFilter(KeyEvent.KEY_TYPED, this::guardInlineHintMutation); codeArea.addEventFilter(KeyEvent.KEY_TYPED, this::guardInlineHintMutation);
@ -229,6 +232,7 @@ public final class EditorWorkspace extends Workspace {
EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation); EditorDocumentPresentationStyles.applyToCodeArea(codeArea, presentation);
refreshCommandSurfaces(fileBuffer); refreshCommandSurfaces(fileBuffer);
statusBar.showFile(projectReference, fileBuffer, presentation); statusBar.showFile(projectReference, fileBuffer, presentation);
refreshStatusBarCaret();
refreshSemanticOutline(fileBuffer, analysis); refreshSemanticOutline(fileBuffer, analysis);
} }
@ -370,6 +374,7 @@ public final class EditorWorkspace extends Workspace {
final String sourceContent = inlineHintProjection.stripDecorations(content); final String sourceContent = inlineHintProjection.stripDecorations(content);
final VfsDocumentOpenResult.VfsTextDocument updatedDocument = vfsProjectDocument.updateDocument(activeFile.path(), sourceContent); final VfsDocumentOpenResult.VfsTextDocument updatedDocument = vfsProjectDocument.updateDocument(activeFile.path(), sourceContent);
openFileSession.open(bufferFrom(updatedDocument)); openFileSession.open(bufferFrom(updatedDocument));
statusBar.showDocumentFormatting(updatedDocument.lineSeparator(), updatedDocument.content());
tabStrip.showOpenFiles( tabStrip.showOpenFiles(
openFileSession.openFiles(), openFileSession.openFiles(),
openFileSession.activeFile().map(EditorOpenFileBuffer::path).orElse(null)); openFileSession.activeFile().map(EditorOpenFileBuffer::path).orElse(null));
@ -473,6 +478,14 @@ public final class EditorWorkspace extends Workspace {
refreshParagraphGraphics(); refreshParagraphGraphics();
} }
private void refreshStatusBarCaret() {
openFileSession.activeFile()
.filter(EditorOpenFileBuffer::editable)
.ifPresent(ignored -> statusBar.showCaretPosition(
codeArea.getCurrentParagraph() + 1,
codeArea.getCaretColumn() + 1));
}
private void guardInlineHintMutation(final KeyEvent event) { private void guardInlineHintMutation(final KeyEvent event) {
if (syncingEditor || !codeArea.isEditable()) { if (syncingEditor || !codeArea.isEditable()) {
return; return;