From d9ba5e3cb4f820a8bce1e1b3dc558700ee215554 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Tue, 31 Mar 2026 07:11:54 +0100 Subject: [PATCH] editor workspace foundation --- .../p/studio/controls/WorkspaceDockPane.java | 230 +++++++ .../java/p/studio/utilities/i18n/I18n.java | 1 + .../p/studio/utilities/logspane/LogsPane.java | 35 +- .../workspaces/editor/EditorHelperPanel.java | 26 +- .../workspaces/editor/EditorOutlinePanel.java | 25 +- .../editor/EditorProjectNavigatorPanel.java | 67 +- .../workspaces/editor/EditorStatusBar.java | 26 +- .../workspaces/editor/EditorTabStrip.java | 30 +- .../workspaces/editor/EditorWorkspace.java | 21 +- .../editor/EditorWorkspaceIcons.java | 10 + .../main/resources/i18n/messages.properties | 1 + .../resources/themes/default-prometeu.css | 127 +++- .../fragments/.studio/activities.json | 625 ++++++++++++++++++ test-projects/main/.studio/activities.json | 520 +++++++-------- 14 files changed, 1431 insertions(+), 313 deletions(-) create mode 100644 prometeu-studio/src/main/java/p/studio/controls/WorkspaceDockPane.java diff --git a/prometeu-studio/src/main/java/p/studio/controls/WorkspaceDockPane.java b/prometeu-studio/src/main/java/p/studio/controls/WorkspaceDockPane.java new file mode 100644 index 00000000..c958889d --- /dev/null +++ b/prometeu-studio/src/main/java/p/studio/controls/WorkspaceDockPane.java @@ -0,0 +1,230 @@ +package p.studio.controls; + +import javafx.application.Platform; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.geometry.Orientation; +import javafx.scene.Node; +import javafx.scene.control.SplitPane; +import javafx.scene.control.TitledPane; + +import java.util.Objects; + +public abstract class WorkspaceDockPane extends TitledPane { + private static final double DEFAULT_MAXIMUM_DOCK_SHARE = 0.50; + private static final double DEFAULT_FALLBACK_HEIGHT = 520.0; + + private final double collapsedHeight; + private final double minimumExpandedHeight; + private final double defaultExpandedHeight; + private final double maximumDockShare; + private final ChangeListener dividerPositionListener = (ignored, previous, current) -> normalizeDivider(); + private boolean syncingDivider; + private SplitPane dockSplit; + private SplitPane.Divider observedDivider; + private Double lastExpandedDividerPosition; + + protected WorkspaceDockPane( + final ObservableValue titleBinding, + final double collapsedHeight, + final double minimumExpandedHeight, + final double defaultExpandedHeight, + final boolean expandedByDefault, + final String... styleClasses) { + this(titleBinding, collapsedHeight, minimumExpandedHeight, defaultExpandedHeight, DEFAULT_MAXIMUM_DOCK_SHARE, expandedByDefault, styleClasses); + } + + protected WorkspaceDockPane( + final ObservableValue titleBinding, + final double collapsedHeight, + final double minimumExpandedHeight, + final double defaultExpandedHeight, + final double maximumDockShare, + final boolean expandedByDefault, + final String... styleClasses) { + this.collapsedHeight = collapsedHeight; + this.minimumExpandedHeight = minimumExpandedHeight; + this.defaultExpandedHeight = defaultExpandedHeight; + this.maximumDockShare = maximumDockShare; + + textProperty().bind(Objects.requireNonNull(titleBinding, "titleBinding")); + getStyleClass().add("workspace-dock-pane"); + if (styleClasses != null && styleClasses.length > 0) { + getStyleClass().addAll(styleClasses); + } + setCollapsible(true); + setExpanded(expandedByDefault); + setAnimated(false); + setMinHeight(expandedByDefault ? minimumExpandedHeight : collapsedHeight); + setPrefHeight(defaultExpandedHeight); + setMaxHeight(expandedByDefault ? Double.MAX_VALUE : collapsedHeight); + expandedProperty().addListener((ignored, previous, expanded) -> { + if (expanded) { + expandDock(); + } else { + collapseDock(); + } + }); + } + + public final double collapsedHeight() { + return collapsedHeight; + } + + public final double minimumExpandedHeight() { + return minimumExpandedHeight; + } + + public final double defaultExpandedHeight() { + return defaultExpandedHeight; + } + + public final SplitPane createBottomDockLayout(final Node primaryContent, final String... splitStyleClasses) { + final var split = new SplitPane(Objects.requireNonNull(primaryContent, "primaryContent"), this); + split.setOrientation(Orientation.VERTICAL); + split.getStyleClass().add("workspace-dock-split"); + if (splitStyleClasses != null && splitStyleClasses.length > 0) { + split.getStyleClass().addAll(splitStyleClasses); + } + SplitPane.setResizableWithParent(primaryContent, true); + SplitPane.setResizableWithParent(this, true); + dockSplit = split; + bindDividerPositionListener(); + dockSplit.heightProperty().addListener((ignored, previous, current) -> { + if (isExpanded()) { + normalizeDivider(); + } else { + applyCollapsedDivider(); + } + }); + dockSplit.getDividers().addListener((javafx.collections.ListChangeListener) change -> { + while (change.next()) { + if (change.wasAdded() && !change.getAddedSubList().isEmpty()) { + bindDividerPositionListener(); + } + } + }); + if (isExpanded()) { + expandDock(); + } else { + collapseDock(); + } + return split; + } + + protected final void setDockContent(final Node content) { + setContent(Objects.requireNonNull(content, "content")); + } + + private void expandDock() { + setMinHeight(minimumExpandedHeight); + setPrefHeight(defaultExpandedHeight); + setMaxHeight(Double.MAX_VALUE); + getStyleClass().remove("workspace-dock-pane-collapsed"); + if (dockSplit != null) { + dockSplit.getStyleClass().remove("workspace-dock-split-collapsed"); + updateDividerInteractivity(false); + final double restored = lastExpandedDividerPosition != null ? lastExpandedDividerPosition : defaultDividerPosition(); + setDivider(clampExpandedDivider(restored)); + } + } + + private void collapseDock() { + setMinHeight(collapsedHeight); + setPrefHeight(collapsedHeight); + setMaxHeight(collapsedHeight); + if (!getStyleClass().contains("workspace-dock-pane-collapsed")) { + getStyleClass().add("workspace-dock-pane-collapsed"); + } + if (dockSplit != null && !dockSplit.getStyleClass().contains("workspace-dock-split-collapsed")) { + dockSplit.getStyleClass().add("workspace-dock-split-collapsed"); + } + if (dockSplit != null) { + applyCollapsedDivider(); + updateDividerInteractivity(true); + } + } + + private void applyCollapsedDivider() { + final double height = currentHeight(); + final double collapsedPosition = Math.max(0.0, 1.0 - (collapsedHeight / height)); + setDivider(Math.min(collapsedPosition, 0.985)); + } + + private void normalizeDivider() { + if (syncingDivider || dockSplit == null || dockSplit.getDividers().isEmpty()) { + return; + } + final double clamped = clampExpandedDivider(currentDivider()); + lastExpandedDividerPosition = clamped; + if (Math.abs(clamped - currentDivider()) > 0.0001) { + setDivider(clamped); + } + } + + private double clampExpandedDivider(final double position) { + final double minDivider = 1.0 - maximumDockShare; + final double maxDivider = Math.max(minDivider, 1.0 - (minimumExpandedHeight / currentHeight())); + if (position < minDivider) { + return minDivider; + } + if (position > maxDivider) { + return maxDivider; + } + return position; + } + + private double defaultDividerPosition() { + final double preferred = 1.0 - (defaultExpandedHeight / currentHeight()); + return clampExpandedDivider(preferred); + } + + private double currentHeight() { + if (dockSplit == null || dockSplit.getHeight() <= 0) { + return DEFAULT_FALLBACK_HEIGHT; + } + return dockSplit.getHeight(); + } + + private double currentDivider() { + return dockSplit == null || dockSplit.getDividers().isEmpty() ? defaultDividerPosition() : dockSplit.getDividers().get(0).getPosition(); + } + + private void setDivider(final double position) { + if (dockSplit == null) { + return; + } + syncingDivider = true; + try { + dockSplit.setDividerPositions(position); + } finally { + syncingDivider = false; + } + } + + private void bindDividerPositionListener() { + if (dockSplit == null || dockSplit.getDividers().isEmpty()) { + return; + } + final var divider = dockSplit.getDividers().get(0); + if (observedDivider == divider) { + return; + } + if (observedDivider != null) { + observedDivider.positionProperty().removeListener(dividerPositionListener); + } + observedDivider = divider; + observedDivider.positionProperty().addListener(dividerPositionListener); + } + + private void updateDividerInteractivity(final boolean disabled) { + if (dockSplit == null) { + return; + } + Platform.runLater(() -> dockSplit.lookupAll(".split-pane-divider").forEach(node -> { + node.setMouseTransparent(disabled); + node.setVisible(!disabled); + node.setManaged(!disabled); + })); + } +} diff --git a/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java b/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java index dc8fa305..4d468927 100644 --- a/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java +++ b/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java @@ -73,6 +73,7 @@ public enum I18n { WORKSPACE_CODE("workspace.code"), CODE_EDITOR_NAVIGATOR_TITLE("codeEditor.navigator.title"), CODE_EDITOR_NAVIGATOR_REFRESH("codeEditor.navigator.refresh"), + CODE_EDITOR_NAVIGATOR_REVEAL_ACTIVE("codeEditor.navigator.revealActive"), CODE_EDITOR_NAVIGATOR_PLACEHOLDER("codeEditor.navigator.placeholder"), CODE_EDITOR_NAVIGATOR_DETAIL("codeEditor.navigator.detail"), CODE_EDITOR_OUTLINE_TITLE("codeEditor.outline.title"), diff --git a/prometeu-studio/src/main/java/p/studio/utilities/logspane/LogsPane.java b/prometeu-studio/src/main/java/p/studio/utilities/logspane/LogsPane.java index e737fc68..27d7b91e 100644 --- a/prometeu-studio/src/main/java/p/studio/utilities/logspane/LogsPane.java +++ b/prometeu-studio/src/main/java/p/studio/utilities/logspane/LogsPane.java @@ -4,14 +4,18 @@ import javafx.application.Platform; import javafx.beans.value.ObservableValue; import javafx.scene.control.TextArea; import javafx.scene.control.TitledPane; +import p.studio.controls.WorkspaceDockPane; import p.studio.controls.lifecycle.StudioControlLifecycle; import p.studio.controls.lifecycle.StudioControlLifecycleSupport; import java.util.Objects; -public abstract class LogsPane implements StudioControlLifecycle { +public abstract class LogsPane extends WorkspaceDockPane implements StudioControlLifecycle { + private static final double COLLAPSED_HEIGHT = 34.0; + private static final double MINIMUM_EXPANDED_HEIGHT = 120.0; + private static final double DEFAULT_EXPANDED_HEIGHT = 180.0; + private final TextArea textArea; - private final TitledPane pane; protected LogsPane( ObservableValue titleBinding, @@ -23,6 +27,14 @@ public abstract class LogsPane implements StudioControlLifecycle { ObservableValue titleBinding, String workspace, boolean installLifecycle) { + super( + Objects.requireNonNull(titleBinding, "titleBinding"), + COLLAPSED_HEIGHT, + MINIMUM_EXPANDED_HEIGHT, + DEFAULT_EXPANDED_HEIGHT, + true, + "%s-workspace-logs-pane".formatted(normalizeWorkspaceName(workspace))); + final String workspaceName = Objects.requireNonNull(workspace, "workspace").trim(); if (workspaceName.isBlank()) { throw new IllegalArgumentException("workspace must not be blank"); @@ -34,19 +46,14 @@ public abstract class LogsPane implements StudioControlLifecycle { textArea.setPrefRowCount(8); textArea.getStyleClass().add("%s-workspace-logs".formatted(workspaceName)); - this.pane = new TitledPane(); - pane.textProperty().bind(Objects.requireNonNull(titleBinding, "titleBinding")); - pane.setContent(textArea); - pane.setCollapsible(true); - pane.setExpanded(true); - pane.getStyleClass().add("%s-workspace-logs-pane".formatted(workspaceName)); + setDockContent(textArea); if (installLifecycle) { - StudioControlLifecycleSupport.install(pane, this); + StudioControlLifecycleSupport.install(this, this); } } public final TitledPane getPane() { - return pane; + return this; } protected final void appendLine(String line) { @@ -60,4 +67,12 @@ public abstract class LogsPane implements StudioControlLifecycle { protected final void clearLogs() { Platform.runLater(textArea::clear); } + + private static String normalizeWorkspaceName(final String workspace) { + final String workspaceName = Objects.requireNonNull(workspace, "workspace").trim(); + if (workspaceName.isBlank()) { + throw new IllegalArgumentException("workspace must not be blank"); + } + return workspaceName; + } } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorHelperPanel.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorHelperPanel.java index ca217510..13b78e8e 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorHelperPanel.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorHelperPanel.java @@ -2,28 +2,34 @@ package p.studio.workspaces.editor; import javafx.geometry.Insets; import javafx.scene.control.Label; -import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import p.studio.Container; +import p.studio.controls.WorkspaceDockPane; import p.studio.utilities.i18n.I18n; -public final class EditorHelperPanel extends BorderPane { +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; public EditorHelperPanel() { - getStyleClass().addAll("editor-workspace-panel", "editor-workspace-helper-panel"); - setPadding(new Insets(14, 16, 14, 16)); - - final var title = new Label(); - title.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_HELPER_TITLE)); - title.getStyleClass().add("editor-workspace-panel-title"); + super( + Container.i18n().bind(I18n.CODE_EDITOR_HELPER_TITLE), + COLLAPSED_HEIGHT, + MINIMUM_EXPANDED_HEIGHT, + DEFAULT_EXPANDED_HEIGHT, + false, + "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); - final var content = new VBox(8, title, placeholder); + final var content = new VBox(8, placeholder); content.getStyleClass().add("editor-workspace-panel-content"); - setCenter(content); + content.setPadding(new Insets(10, 16, 14, 16)); + setDockContent(content); } } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorOutlinePanel.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorOutlinePanel.java index 35fc1ec8..85fea049 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorOutlinePanel.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorOutlinePanel.java @@ -2,28 +2,33 @@ package p.studio.workspaces.editor; import javafx.geometry.Insets; import javafx.scene.control.Label; -import javafx.scene.layout.BorderPane; import javafx.scene.layout.VBox; import p.studio.Container; +import p.studio.controls.WorkspaceDockPane; import p.studio.utilities.i18n.I18n; -public final class EditorOutlinePanel extends BorderPane { +public final class EditorOutlinePanel extends WorkspaceDockPane { + private static final double COLLAPSED_HEIGHT = 34.0; + private static final double MINIMUM_EXPANDED_HEIGHT = 120.0; + private static final double DEFAULT_HEIGHT = 180.0; public EditorOutlinePanel() { - getStyleClass().addAll("editor-workspace-panel", "editor-workspace-outline-panel"); - setPadding(new Insets(14, 16, 14, 16)); - - final var title = new Label(); - title.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_OUTLINE_TITLE)); - title.getStyleClass().add("editor-workspace-panel-title"); + super( + Container.i18n().bind(I18n.CODE_EDITOR_OUTLINE_TITLE), + COLLAPSED_HEIGHT, + MINIMUM_EXPANDED_HEIGHT, + DEFAULT_HEIGHT, + true, + "editor-workspace-outline-panel"); final var placeholder = new Label(); placeholder.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_OUTLINE_PLACEHOLDER)); placeholder.getStyleClass().add("editor-workspace-placeholder"); placeholder.setWrapText(true); - final var content = new VBox(8, title, placeholder); + final var content = new VBox(8, placeholder); content.getStyleClass().add("editor-workspace-panel-content"); - setCenter(content); + content.setPadding(new Insets(10, 16, 14, 16)); + setDockContent(content); } } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorProjectNavigatorPanel.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorProjectNavigatorPanel.java index b4e9ae5f..c45b4904 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorProjectNavigatorPanel.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorProjectNavigatorPanel.java @@ -3,6 +3,7 @@ package p.studio.workspaces.editor; import javafx.geometry.Insets; import javafx.scene.control.Button; import javafx.scene.control.Label; +import javafx.scene.control.Tooltip; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; @@ -23,10 +24,12 @@ import java.util.Set; import java.util.function.Consumer; public final class EditorProjectNavigatorPanel extends BorderPane { + private final Button revealActiveButton = new Button(); private final Button refreshButton = new Button(); private final TreeView treeView = new TreeView<>(); private final Label emptyState = emptyLabel(); private final StackPane content = new StackPane(); + private Runnable revealActiveFileAction = () -> { }; private Runnable refreshAction = () -> { }; private Consumer fileSelectionAction = node -> { }; @@ -46,15 +49,29 @@ public final class EditorProjectNavigatorPanel extends BorderPane { title.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_NAVIGATOR_TITLE)); title.getStyleClass().add("editor-workspace-panel-title"); - refreshButton.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_NAVIGATOR_REFRESH)); + revealActiveButton.setFocusTraversable(false); + revealActiveButton.setDisable(true); + revealActiveButton.getStyleClass().addAll( + "studio-button", + "studio-button-icon", + "editor-workspace-header-icon-button"); + revealActiveButton.setGraphic(EditorWorkspaceIcons.target()); + revealActiveButton.setTooltip(new Tooltip(Container.i18n().text(I18n.CODE_EDITOR_NAVIGATOR_REVEAL_ACTIVE))); + revealActiveButton.setOnAction(event -> revealActiveFileAction.run()); + refreshButton.setFocusTraversable(false); - refreshButton.getStyleClass().addAll("studio-button", "studio-button-secondary"); + refreshButton.getStyleClass().addAll( + "studio-button", + "studio-button-icon", + "editor-workspace-header-icon-button"); + refreshButton.setGraphic(EditorWorkspaceIcons.refresh()); + refreshButton.setTooltip(new Tooltip(Container.i18n().text(I18n.CODE_EDITOR_NAVIGATOR_REFRESH))); refreshButton.setOnAction(event -> refreshAction.run()); final var spacer = new Region(); HBox.setHgrow(spacer, Priority.ALWAYS); - final var header = new HBox(8, title, spacer, refreshButton); + final var header = new HBox(8, title, spacer, revealActiveButton, refreshButton); header.getStyleClass().add("editor-workspace-panel-header"); return header; } @@ -96,6 +113,14 @@ public final class EditorProjectNavigatorPanel extends BorderPane { this.refreshAction = Objects.requireNonNull(refreshAction, "refreshAction"); } + public void setRevealActiveFileAction(final Runnable revealActiveFileAction) { + this.revealActiveFileAction = Objects.requireNonNull(revealActiveFileAction, "revealActiveFileAction"); + } + + public void setRevealActiveFileAvailable(final boolean available) { + revealActiveButton.setDisable(!available); + } + public void setFileSelectionAction(final Consumer fileSelectionAction) { this.fileSelectionAction = Objects.requireNonNull(fileSelectionAction, "fileSelectionAction"); } @@ -109,11 +134,26 @@ public final class EditorProjectNavigatorPanel extends BorderPane { showEmptyState(false); } + public void revealPath(final Path path) { + final var normalizedPath = Objects.requireNonNull(path, "path").toAbsolutePath().normalize(); + final var rootItem = treeView.getRoot(); + if (rootItem == null) { + return; + } + findTreeItem(rootItem, normalizedPath).ifPresent(item -> { + expandAncestors(item); + treeView.getSelectionModel().select(item); + treeView.scrollTo(Math.max(treeView.getRow(item) - 2, 0)); + }); + } + private TreeItem buildTreeItem( final EditorProjectNode node, final Set expandedPaths, final boolean isRoot) { - final var item = new TreeItem<>(node); + final TreeItem item = node.directory() + ? new DirectoryTreeItem(node) + : new TreeItem<>(node); item.setExpanded(isRoot || expandedPaths.contains(node.path())); node.children().stream() .map(child -> buildTreeItem(child, expandedPaths, false)) @@ -176,6 +216,14 @@ public final class EditorProjectNavigatorPanel extends BorderPane { emptyState.setManaged(empty); } + private void expandAncestors(final TreeItem item) { + TreeItem current = item; + while (current != null) { + current.setExpanded(true); + current = current.getParent(); + } + } + private Node iconFor(final EditorProjectNode node, final TreeItem treeItem) { if (node.directory()) { if (isBuildTone(node)) { @@ -231,4 +279,15 @@ public final class EditorProjectNavigatorPanel extends BorderPane { placeholder.setWrapText(true); return placeholder; } + + private static final class DirectoryTreeItem extends TreeItem { + private DirectoryTreeItem(final EditorProjectNode node) { + super(node); + } + + @Override + public boolean isLeaf() { + return false; + } + } } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java index ad324704..7dfbb2d3 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorStatusBar.java @@ -58,6 +58,7 @@ public final class EditorStatusBar extends HBox { public void showFile(final ProjectReference projectReference, final EditorOpenFileBuffer fileBuffer) { showBreadcrumb(projectReference, fileBuffer.path()); + showMetadata(true); bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION); setText(lineSeparator, fileBuffer.lineSeparator()); bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION); @@ -66,7 +67,8 @@ public final class EditorStatusBar extends HBox { } public void showPlaceholder() { - breadcrumb.getChildren().setAll(placeholderLabel()); + breadcrumb.getChildren().clear(); + showMetadata(false); bindDefault(position, I18n.CODE_EDITOR_STATUS_POSITION); bindDefault(lineSeparator, I18n.CODE_EDITOR_STATUS_LINE_SEPARATOR); bindDefault(indentation, I18n.CODE_EDITOR_STATUS_INDENTATION); @@ -83,12 +85,6 @@ public final class EditorStatusBar extends HBox { label.textProperty().bind(Container.i18n().bind(key)); } - private Label placeholderLabel() { - final var label = new Label(); - label.textProperty().bind(Container.i18n().bind(I18n.CODE_EDITOR_STATUS_BREADCRUMB)); - return label; - } - private void showBreadcrumb(final ProjectReference projectReference, final Path filePath) { breadcrumb.getChildren().clear(); breadcrumb.getChildren().add(segment( @@ -119,11 +115,10 @@ public final class EditorStatusBar extends HBox { } private String rootLabel(final ProjectReference projectReference) { - final var directoryName = Optional.ofNullable(projectReference.rootPath().getFileName()) + return Optional.ofNullable(projectReference.rootPath().getFileName()) .map(Path::toString) .filter(name -> !name.isBlank()) .orElse(projectReference.name()); - return directoryName + " (" + projectReference.name() + ")"; } private HBox segment(final String text, final Node icon, final boolean root) { @@ -167,6 +162,19 @@ public final class EditorStatusBar extends HBox { label.setText(text); } + private void showMetadata(final boolean visible) { + setVisibleManaged(position, visible); + setVisibleManaged(lineSeparator, visible); + setVisibleManaged(indentation, visible); + setVisibleManaged(language, visible); + setVisibleManaged(readOnly, visible); + } + + private void setVisibleManaged(final Label label, final boolean visible) { + label.setVisible(visible); + label.setManaged(visible); + } + private void styleChip(final Label label) { if (!label.getStyleClass().contains("editor-workspace-status-chip")) { label.getStyleClass().add("editor-workspace-status-chip"); diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java index d255519d..b686d04b 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorTabStrip.java @@ -2,6 +2,8 @@ package p.studio.workspaces.editor; import javafx.geometry.Pos; import javafx.scene.control.Button; +import javafx.scene.control.CustomMenuItem; +import javafx.scene.control.Label; import javafx.scene.control.MenuButton; import javafx.scene.control.MenuItem; import javafx.scene.control.OverrunStyle; @@ -21,7 +23,8 @@ public final class EditorTabStrip extends HBox { private static final double TAB_WIDTH_HINT = 176.0; private static final double TAB_HEIGHT_HINT = 34.0; private static final double STRIP_HEIGHT_HINT = 50.0; - private static final double OVERFLOW_WIDTH_HINT = 110.0; + private static final double OVERFLOW_WIDTH_HINT = 128.0; + private static final double OVERFLOW_MENU_WIDTH_HINT = 180.0; private final MenuButton overflowButton = new MenuButton(); private final Region spacer = new Region(); @@ -44,6 +47,7 @@ public final class EditorTabStrip extends HBox { "studio-button", "studio-button-secondary", "editor-workspace-tab-overflow"); + applyOverflowMetrics(); widthProperty().addListener((ignored, previous, current) -> rebuild()); rebuild(); } @@ -107,7 +111,19 @@ public final class EditorTabStrip extends HBox { continue; } final var fileBuffer = openFiles.get(index); - final var item = new MenuItem(fileBuffer.tabLabel()); + final var label = new Label(fileBuffer.tabLabel()); + label.getStyleClass().add("editor-workspace-tab-overflow-item"); + label.setMinWidth(OVERFLOW_MENU_WIDTH_HINT); + label.setPrefWidth(OVERFLOW_MENU_WIDTH_HINT); + label.setMaxWidth(OVERFLOW_MENU_WIDTH_HINT); + label.setMinHeight(TAB_HEIGHT_HINT); + label.setPrefHeight(TAB_HEIGHT_HINT); + label.setMaxHeight(TAB_HEIGHT_HINT); + label.setAlignment(Pos.CENTER_LEFT); + label.setTextOverrun(OverrunStyle.ELLIPSIS); + + final var item = new CustomMenuItem(label, true); + item.getStyleClass().add("editor-workspace-tab-overflow-menu-item"); item.setOnAction(event -> tabSelectionAction.accept(fileBuffer.path())); items.add(item); } @@ -142,4 +158,14 @@ public final class EditorTabStrip extends HBox { button.setTextOverrun(OverrunStyle.ELLIPSIS); button.setMnemonicParsing(false); } + + private void applyOverflowMetrics() { + overflowButton.setMinWidth(OVERFLOW_WIDTH_HINT); + overflowButton.setPrefWidth(OVERFLOW_WIDTH_HINT); + overflowButton.setMaxWidth(OVERFLOW_WIDTH_HINT); + overflowButton.setMinHeight(TAB_HEIGHT_HINT); + overflowButton.setPrefHeight(TAB_HEIGHT_HINT); + overflowButton.setMaxHeight(TAB_HEIGHT_HINT); + overflowButton.setMnemonicParsing(false); + } } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java index 13032cbf..4cfd2686 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspace.java @@ -34,6 +34,7 @@ public final class EditorWorkspace extends Workspace { codeArea.getStyleClass().add("editor-workspace-code-area"); showEditorPlaceholder(); navigatorPanel.setRefreshAction(this::refreshNavigator); + navigatorPanel.setRevealActiveFileAction(this::revealActiveFileInNavigator); navigatorPanel.setFileSelectionAction(this::openNode); tabStrip.setTabSelectionAction(path -> { openFileSession.activate(path); @@ -76,6 +77,7 @@ public final class EditorWorkspace extends Workspace { private void renderSession() { final var activeFile = openFileSession.activeFile(); + navigatorPanel.setRevealActiveFileAvailable(activeFile.isPresent()); tabStrip.showOpenFiles( openFileSession.openFiles(), activeFile.map(EditorOpenFileBuffer::path).orElse(null)); @@ -90,6 +92,12 @@ public final class EditorWorkspace extends Workspace { statusBar.showFile(projectReference, fileBuffer); } + private void revealActiveFileInNavigator() { + openFileSession.activeFile() + .map(EditorOpenFileBuffer::path) + .ifPresent(navigatorPanel::revealPath); + } + private void showUnsupportedFileModal(final java.nio.file.Path path) { final var alert = new Alert(Alert.AlertType.INFORMATION); if (root.getScene() != null) { @@ -109,20 +117,19 @@ public final class EditorWorkspace extends Workspace { } private VBox buildLayout() { - final var content = new SplitPane(buildLeftColumn(), buildCenterColumn()); + final var content = new SplitPane(buildLeftColumn(), buildRightColumn()); content.setDividerPositions(0.30); content.getStyleClass().add("editor-workspace-split"); - final var layout = new VBox(12, content, helperPanel, statusBar); + final var layout = new VBox(12, content, statusBar); layout.getStyleClass().add("editor-workspace-layout"); VBox.setVgrow(content, Priority.ALWAYS); return layout; } - private VBox buildLeftColumn() { - final var leftColumn = new VBox(12, navigatorPanel, outlinePanel); + private SplitPane buildLeftColumn() { + final var leftColumn = outlinePanel.createBottomDockLayout(navigatorPanel, "editor-workspace-left-split"); leftColumn.getStyleClass().add("editor-workspace-left-column"); - VBox.setVgrow(navigatorPanel, Priority.ALWAYS); return leftColumn; } @@ -132,4 +139,8 @@ public final class EditorWorkspace extends Workspace { VBox.setVgrow(codeArea, Priority.ALWAYS); return centerColumn; } + + private SplitPane buildRightColumn() { + return helperPanel.createBottomDockLayout(buildCenterColumn(), "editor-workspace-right-split"); + } } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspaceIcons.java b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspaceIcons.java index d63b063c..84c54c2e 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspaceIcons.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/editor/EditorWorkspaceIcons.java @@ -8,6 +8,8 @@ public final class EditorWorkspaceIcons { 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"; + private static final String REFRESH_PATH = "M8 2.2a5.8 5.8 0 0 1 4.3 1.9V2.8h1.2v3.8H9.7V5.4h1.8A4.6 4.6 0 1 0 12 11h1.3A5.9 5.9 0 1 1 8 2.2z"; + private static final String TARGET_PATH = "M7.25 1.5h1.5v1.9a4.85 4.85 0 0 1 3.85 3.85h1.9v1.5h-1.9a4.85 4.85 0 0 1-3.85 3.85v1.9h-1.5v-1.9A4.85 4.85 0 0 1 3.4 8.75H1.5v-1.5h1.9A4.85 4.85 0 0 1 7.25 3.4zm.75 3.25A3.25 3.25 0 1 0 8 11.25 3.25 3.25 0 0 0 8 4.75zm0 1.65a1.6 1.6 0 1 1 0 3.2 1.6 1.6 0 0 1 0-3.2z"; private EditorWorkspaceIcons() { } @@ -32,6 +34,14 @@ public final class EditorWorkspaceIcons { return icon(COG_PATH, "editor-workspace-icon-cog"); } + public static Node refresh() { + return icon(REFRESH_PATH, "editor-workspace-icon-refresh"); + } + + public static Node target() { + return icon(TARGET_PATH, "editor-workspace-icon-target"); + } + private static Node icon(final String path, final String toneClass) { final var shape = new SVGPath(); shape.setContent(path); diff --git a/prometeu-studio/src/main/resources/i18n/messages.properties b/prometeu-studio/src/main/resources/i18n/messages.properties index c201578f..deb38280 100644 --- a/prometeu-studio/src/main/resources/i18n/messages.properties +++ b/prometeu-studio/src/main/resources/i18n/messages.properties @@ -64,6 +64,7 @@ toolbar.export=Export workspace.code=Code codeEditor.navigator.title=Project Navigator codeEditor.navigator.refresh=Refresh +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 diff --git a/prometeu-studio/src/main/resources/themes/default-prometeu.css b/prometeu-studio/src/main/resources/themes/default-prometeu.css index 6af7836b..6cb249b4 100644 --- a/prometeu-studio/src/main/resources/themes/default-prometeu.css +++ b/prometeu-studio/src/main/resources/themes/default-prometeu.css @@ -351,6 +351,27 @@ -fx-background-color: transparent; } +.editor-workspace-split > .split-pane-divider { + -fx-background-color: transparent; + -fx-padding: 0 8 0 8; +} + +.workspace-dock-split { + -fx-background-color: transparent; +} + +.workspace-dock-split > .split-pane-divider { + -fx-background-color: #243243; + -fx-border-color: #556f8b; + -fx-padding: 3 0 3 0; +} + +.workspace-dock-split-collapsed > .split-pane-divider { + -fx-background-color: transparent; + -fx-border-color: transparent; + -fx-padding: 0; +} + .editor-workspace-left-column, .editor-workspace-center-column { -fx-spacing: 12; @@ -367,6 +388,19 @@ -fx-alignment: center-left; } +.editor-workspace-header-icon-button { + -fx-background-color: transparent; + -fx-border-color: transparent; + -fx-background-radius: 4; + -fx-border-radius: 4; + -fx-padding: 6; +} + +.editor-workspace-header-icon-button:hover { + -fx-background-color: #243243; + -fx-border-color: #556f8b; +} + .editor-workspace-panel-content { -fx-padding: 6 0 0 0; } @@ -399,7 +433,7 @@ .editor-workspace-tree .tree-cell { -fx-background-color: transparent; -fx-text-fill: #d6e0ea; - -fx-font-size: 13px; + -fx-font-size: 14px; -fx-padding: 4 6 4 4; } @@ -408,6 +442,18 @@ -fx-text-fill: #ffffff; } +.editor-workspace-tree .tree-disclosure-node { + -fx-padding: 0 4 0 0; +} + +.editor-workspace-tree .tree-disclosure-node .arrow { + -fx-background-color: #8ea4ba; +} + +.editor-workspace-tree .tree-cell:selected .tree-disclosure-node .arrow { + -fx-background-color: #e8f3ff; +} + .editor-workspace-tree-cell-tagged { -fx-font-weight: bold; } @@ -452,8 +498,16 @@ -fx-fill: #c6d0da; } +.editor-workspace-icon-refresh { + -fx-fill: #eff5fb; +} + +.editor-workspace-icon-target { + -fx-fill: #eff5fb; +} + .editor-workspace-outline-panel { - -fx-min-height: 150px; + -fx-min-height: 0; } .editor-workspace-tab-strip { @@ -533,14 +587,78 @@ -fx-background-radius: 0; -fx-border-radius: 0; -fx-padding: 0 12 0 12; + -fx-min-width: 128; + -fx-pref-width: 128; + -fx-max-width: 128; + -fx-min-height: 34; + -fx-pref-height: 34; + -fx-max-height: 34; +} + +.editor-workspace-tab-overflow-item { + -fx-text-fill: #d9e2eb; + -fx-font-size: 12px; + -fx-padding: 0 10 0 10; + -fx-background-color: transparent; } .editor-workspace-code-area .content { -fx-background-color: #171c22; } +.workspace-dock-pane { + -fx-collapsible: true; + -fx-background-color: transparent; + -fx-border-color: transparent; + -fx-background-insets: 0; + -fx-border-insets: 0; +} + +.workspace-dock-pane > .title { + -fx-background-color: #1b1f25; + -fx-border-color: #2a313c; + -fx-background-radius: 0; + -fx-border-radius: 0; + -fx-background-insets: 0; + -fx-border-insets: 0; + -fx-padding: 5 12 5 12; +} + +.workspace-dock-pane > .title > .text { + -fx-fill: #eff5fb; + -fx-font-size: 14px; + -fx-font-weight: bold; +} + +.workspace-dock-pane > .title > .arrow-button { + -fx-padding: 0 6 0 0; +} + +.workspace-dock-pane > .content { + -fx-background-color: #1b1f25; + -fx-border-color: #2a313c; + -fx-background-insets: 0; + -fx-border-insets: 0; +} + .editor-workspace-helper-panel { - -fx-min-height: 96px; + -fx-min-height: 0; +} + +.workspace-dock-pane-collapsed { + -fx-background-insets: 0; +} + +.workspace-dock-pane-collapsed > .title { + -fx-padding: 4 12 4 12; +} + +.workspace-dock-pane-collapsed > .content { + -fx-padding: 0; + -fx-background-color: transparent; + -fx-border-color: transparent; + -fx-background-insets: 0; + -fx-border-insets: 0; } .editor-workspace-status-bar { @@ -549,6 +667,9 @@ -fx-background-radius: 12; -fx-border-radius: 12; -fx-border-color: #2a313c; + -fx-min-height: 40; + -fx-pref-height: 40; + -fx-max-height: 40; } .editor-workspace-status-breadcrumb { diff --git a/test-projects/fragments/.studio/activities.json b/test-projects/fragments/.studio/activities.json index 62dbf5fb..ed82ed58 100644 --- a/test-projects/fragments/.studio/activities.json +++ b/test-projects/fragments/.studio/activities.json @@ -1,4 +1,629 @@ [ { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project ready", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project ready", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project ready", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project ready", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project ready", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { "source" : "Assets", "message" : "8 assets loaded", "severity" : "SUCCESS", diff --git a/test-projects/main/.studio/activities.json b/test-projects/main/.studio/activities.json index 94955859..a0414181 100644 --- a/test-projects/main/.studio/activities.json +++ b/test-projects/main/.studio/activities.json @@ -53,6 +53,266 @@ "message" : "Asset scan started", "severity" : "INFO", "sticky" : false +}, { + "source" : "Assets", + "message" : "0 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project ready", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project ready", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project loading started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Studio", + "message" : "Project opened", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "8 assets loaded", + "severity" : "SUCCESS", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan diagnostics updated.", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bla", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: one-more-atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: bbb2", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: ui_atlas", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Bigode", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Discovered asset: Zelda", + "severity" : "INFO", + "sticky" : false +}, { + "source" : "Assets", + "message" : "Asset scan started", + "severity" : "INFO", + "sticky" : false }, { "source" : "Assets", "message" : "8 assets loaded", @@ -2238,264 +2498,4 @@ "message" : "Discovered asset: ui_atlas", "severity" : "INFO", "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bbb2", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bbb2", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bbb2", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bbb2", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bbb2", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: Bigode", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan started", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "7 assets loaded", - "severity" : "SUCCESS", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Asset scan diagnostics updated.", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bla", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: ui_atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: one-more-atlas", - "severity" : "INFO", - "sticky" : false -}, { - "source" : "Assets", - "message" : "Discovered asset: bbb2", - "severity" : "INFO", - "sticky" : false } ] \ No newline at end of file